将参数传递到中间件的下一个句柄func



我想制作一个可重复使用的中间件,用于整个API的验证。在这里,验证是通过govalidator完成的,所以我只需要传递验证规则和请求映射到的DTO。

func ValidationMiddleware(next http.HandlerFunc, validationRules govalidator.MapData, dto interface{}) http.HandlerFunc {
return func(rw http.ResponseWriter, r *http.Request) {
utils.SetResponseHeaders(rw)
opts := govalidator.Options{
Request:         r,
Data:            &dto,
Rules:           validationRules,
RequiredDefault: true,
}
v := govalidator.New(opts)
err := v.ValidateJSON()
if err != nil {
fmt.Println("Middleware found an error")
err := utils.ErrorWrapper{
StatusCode: 400,
Type:       "Bad Request",
Message:    err,
}
err.ThrowError(rw)
return
}
next(rw, r)
}
}

这就是HandleFunc的样子:

var rules govalidator.MapData = govalidator.MapData{
"name":        []string{"required"},
"description": []string{"required"},
"price":       []string{"required", "float"},
}
func RegisterItemsRouter(router *mux.Router) {
itemsRouter := router.PathPrefix("/inventory").Subrouter()
itemsRouter.HandleFunc("/", middleware.ValidationMiddleware(addItem, rules, dto.CreateItem{
Name:        "",
Description: "",
Price:       govalidator.Float64{},
})).Methods("POST")
}

如果没有发现错误,govalidator会将请求正文解析到dto结构中,因此我希望将其传递到下一个处理程序中,避免再次解析正文。

如何将此结构传递给下一个HandleFunc?

从代码中,它看起来像是将请求传递给验证器选项,验证器从中读取并验证主体。这带来了几个问题:HTTP请求只能读取一次,所以验证器会以某种方式返回未分组的对象,或者在验证之前必须读取正文

对于第二个解决方案,第一件事是验证器必须知道它必须解组到的对象的类型:

func ValidationMiddleware(next http.HandlerFunc, factory func() interface{}, validationRules govalidator.MapData, dto interface{}) http.HandlerFunc {
return func(rw http.ResponseWriter, r *http.Request) {
newInstance:=factory()
data, err:=ioutil.ReadAll(r.Body)
json.Unmarshal(data,newInstance)
// Validate newInstance here
r.WithContext(context.WithValue(r.Context(),"data",newInstance))
next(wr,r)
}
}

其中,func factory是一个函数,用于创建将为请求取消组织的对象的实例:

func RegisterItemsRouter(router *mux.Router) {
itemsRouter := router.PathPrefix("/inventory").Subrouter()
itemsRouter.HandleFunc("/", middleware.ValidationMiddleware(addItem, func() interface{} { return &TheStruct{}}, rules, dto.CreateItem{
Name:        "",
Description: "",
Price:       govalidator.Float64{},
})).Methods("POST")
}

这样,当一个新的请求到来时,TheStruct的一个新实例将被创建和取消组合,然后进行验证。如果验证是可以的,它将被放入上下文中,这样下一个中间件或处理程序就可以获得它:

func handler(wr http.ResponseWriter,r *http.Request) {
item:=r.Context().Value("data").(*TheStruct)
...
}

最新更新