5 回答
TA贡献1906条经验 获得超3个赞
您可以将通用逻辑移至单独的函数,并将每个处理程序中特定的所有内容传递给它。
假设您具有以下类型和功能:
type CreateGuestRequest struct{}
type UpdateGuestRequest struct{}
type CreateGuestResponse struct{}
type UpdateGuestResponse struct{}
func CreateGuest(v *CreateGuestRequest) (resp *CreateGuestResponse, err error) {
return nil, nil
}
func UpdateGuest(v *UpdateGuestRequest) (resp *UpdateGuestResponse, err error) {
return nil, nil
}
允许泛型
如果允许泛型,您可以将所有代码从处理程序中分解出来:
func handle[Req any, Resp any](w http.ResponseWriter, r *http.Request, logicFunc func(dst Req) (Resp, error)) {
var dst Req
if err := json.NewDecoder(r.Body).Decode(&dst); err != nil {
log.Printf("Decoding body failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := logicFunc(dst)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Encoding response failed: %v", err)
}
}
func createGuestHandler(w http.ResponseWriter, r *http.Request) {
handle(w, r, CreateGuest)
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
handle(w, r, UpdateGuest)
}
如您所见,所有处理程序实现都只是一行!我们现在甚至可以摆脱处理程序函数,因为我们可以从逻辑函数(如CreateGuest(), UpdateGuest())创建处理程序。
这就是它的样子:
func createHandler[Req any, Resp any](logicFunc func(dst Req) (Resp, error)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var dst Req
if err := json.NewDecoder(r.Body).Decode(&dst); err != nil {
log.Printf("Decoding body failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := logicFunc(dst)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Encoding response failed: %v", err)
}
}
}
并使用它:
func NewGuestMux() *GuestMux {
var guestMux = &GuestMux{}
guestMux.HandleFunc("/guest/createguest", createHandler(CreateGuest))
guestMux.HandleFunc("/guest/updateguest", createHandler(UpdateGuest))
return guestMux
}
没有泛型
此解决方案不使用泛型(也适用于旧的 Go 版本)。
func handle(w http.ResponseWriter, r *http.Request, dst interface{}, logicFunc func() (interface{}, error)) {
if err := json.NewDecoder(r.Body).Decode(dst); err != nil {
log.Printf("Decoding body failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := logicFunc()
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Encoding response failed: %v", err)
}
}
func createGuestHandler(w http.ResponseWriter, r *http.Request) {
var createGuestReq CreateGuestRequest
handle(w, r, &createGuestReq, func() (interface{}, error) {
return CreateGuest(&createGuestReq)
})
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
var updateGuestReq UpdateGuestRequest
handle(w, r, &updateGuestReq, func() (interface{}, error) {
return UpdateGuest(&updateGuestReq)
})
}
TA贡献1801条经验 获得超16个赞
我有这些实用功能 : decodeJsonBody,respondJson我用它来简化响应,而不会增加太多复杂性。我将它包装在Response用于发送客户端错误详细信息的结构中。
type Response struct {
Data interface{} `json:"data"`
Errors interface{} `json:"errors"`
}
func respondJson(w http.ResponseWriter, data interface{}, err error) {
w.Header().Set("Content-Type", "application/json")
if err != nil {
w.WriteHeader(http.StatusBadRequest)
err = json.NewEncoder(w).Encode(Response{
Errors: err.Error(),
})
return
}
err = json.NewEncoder(w).Encode(Response{
Data: data,
})
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("http handler failed to convert response to json %s\n", err)
}
}
func decodeJsonBody(r *http.Request, v interface{}) error {
decoder := json.NewDecoder(r.Body)
return decoder.Decode(v)
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
var updateGuestReq UpdateGuestRequest
err := decodeJsonBody(r, &updeateGuestReq)
if err != nil {
respondJson(w, nil, err)
return
}
data, err := UpdateGuest(&updateGuestReq)
respondJson(w, data, err)
}
TA贡献1784条经验 获得超8个赞
通常 REST API 的/guest
端点只有一个处理程序,它根据HTTP 方法决定要做什么:
POST
去创造GET
检索PUT
更新整个记录PATCH
更新某些字段
您可以查看r.Method
处理程序内部并根据它决定运行什么代码。
如果您绑定到问题中显示的接口,您可以将处理程序包装到具有预期接口的匿名函数,并使其接受一个额外的参数来决定要做什么,如下所示:
guestMux.HandleFunc("/guest/createguest", func(w http.ResponseWriter, r *http.Request) {
guestHandler(r, w, CREATE)
})
guestMux.HandleFunc("/guest/updateguest", func(w http.ResponseWriter, r *http.Request) {
guestHandler(r, w, UPDATE)
})
...
(其中 CREATE 和 UPDATE 是某种标志,告诉guestHandler()它应该做什么)
TA贡献1820条经验 获得超9个赞
这里有很多方法可以避免重复,例如,您可以使用装饰器模式,您可以在其中定义如何解码/编码以及其他不包含您的业务逻辑的步骤。
您可以查看两种有趣的方法:一种来自 Mat:https ://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html
另一个是 go-kit 包(你可以在 github 上查看),但我建议你查看关于如何编写装饰器的想法而不是安装库,这可能对你的实现来说是一种矫枉过正。
- 5 回答
- 0 关注
- 121 浏览
添加回答
举报