如何尽量减少重复代码在去Mux时总是试图返回相同的响应结构?



我有大量类似于以下代码片段的代码,我只是尝试填充我的响应结构,json封接输出,设置状态码并返回结果:

if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
response := responses.UserResponse{
Status:  http.StatusBadRequest,
Message: "error",
Data:    map[string]interface{}{"error": err.Error()},
}
rw.WriteHeader(http.StatusBadRequest)
errRes, _ := json.Marshal(response)
rw.Write(errRes)
return
}

我试图创建一个接收r变量(request.http)的函数来接收响应的主体和状态代码。但注意到,我必须再次检查函数外的错误代码,然后再次执行相同的响应创建流程。

一个精通Go的人如何尽量减少这样的代码重复?首先,这样的代码复制是可以的吗?

通过将decode调用和错误处理移动到可重用函数来减少代码重复:

// Decode returns true if the request body is successfully decoded
// to the value pointed to by pv. Otherwise, decode writes an error
// response and returns false.
func decode(rw http.ResponseWriter, r *http.Request, pv interface{}) bool {
err := json.NewDecoder(r.Body).Decode(pv)
if err == nil {
return true
}
rw.WriteHeader(http.StatusBadRequest)
json.NewEncoder(rw).Encode(map[string]any{
"status":  http.StatusBadRequest,
"message": "error",
"data":    map[string]any{"error": err.Error()},
})
return false
}

像这样使用函数:

func userHandler(rw http.ResponseWriter, r *http.Request) {
var u UserRequest
if !decode(rw, r, &u) {
return
}
}

最好抽象细节,以提供处理程序所做的高级图片。

func (h *rideHandler) handleCancelRideByPassenger(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
user := getUser(ctx)
req := &cancelRequest{}
if err := decode(r, req); err != nil {
h.logger.Error("cancel ride: problem while decoding body request", zap.String("ip", r.RemoteAddr), zap.Error(err))
h.respond.BadRequest(w, NewRESTError(reasonDecoding, "problem while decoding input parameters"))
return
}
req.PublicID = chi.URLParam(r, "id")
err := h.rideService.CancelRide(ctx, req, user)
if err != nil {
var validationErr *ValidationError
switch {
case errors.As(err, &validationErr):
h.respond.BadRequest(w, NewRESTValidationError(reasonValidation, "problem while validating request", validationErr))
return
default:
h.respond.InternalServerError(w, NewRESTError(reasonInternalError, "unknown problem occurred"))
return
}
}
h.respond.Ok(w, NewRESTResponse(&cancelRideResponse{Success: true}))
}

Handler使用一些方便的糖函数来删除重复,并提供处理程序所做工作的高级概述,而不是底层细节。

func decode(request *http.Request, val interface{}) error {
dec := json.NewDecoder(request.Body)
dec.DisallowUnknownFields()
return dec.Decode(val)
}

type Responder struct {
Encoder Encoder
Before BeforeFunc
After AfterFunc
OnError OnErrorFunc
}
func (r *Responder) writeResponse(w http.ResponseWriter, v interface{}, status int) {
if r.Before != nil {
status, v = r.Before(w, v, status)
}
encoder := JSON
if r.Encoder != nil {
encoder = r.Encoder
}
w.Header().Set("Content-Type", encoder.ContentType())
w.WriteHeader(status)
if err := encoder.Encode(w, v); err != nil {
if r.OnError != nil {
r.OnError(err)
}
}
if r.After != nil {
r.After(v, status)
}
}
func (r *Responder) Ok(w http.ResponseWriter, v interface{}) {
r.writeResponse(w, v, http.StatusOK)
}

也许您应该编写自己的响应包或检查开放源代码中可用的响应包。然后,您可以在任何地方使用具有相同响应结构的响应包。

最新更新