在中间件中绑定查询

Bind Query inside middleware

我正在尝试编写一个“Binder”中间件,它将使用带有 gin 的结构类型验证任何请求查询 bindings/validators

例如,假设我有一个名为 /api/subject 的端点组,它要求查询字符串具有主题代码和一个 ID,将使用以下结构(称为 entity.Subject ):

type Subject struct {
    Code string `binding:"required,alphanum"`
    ID   string `binding:"required,alphanum,len=4"`
}

这只是一个示例,但我希望能够将任何结构类型传递给此中间件,因为我希望在未来的处理程序上访问查询数据而不必担心查询验证。

所以我尝试了这样的事情:

func Binder(t reflect.Type) gin.HandlerFunc {
    return func(c *gin.Context) {
        obj := reflect.New(t).Elem().Interface()
        if err := c.BindQuery(&obj); err != nil {
            c.AbortWithStatus(http.StatusBadRequest)
            return
        }

        c.Set(t.Name(), obj)
    }
}

并像这样添加了这个中间件:

apiGroup := router.Group("/api")
{
    // other subgroups/endpoints
    // ...
    subjectGroup := apiGroup.Group("/subject", middleware.Binder(reflect.TypeOf(entity.Subject{})))
}

稍后,在另一个处理程序函数中,假设 GetSubject,我想访问通过执行 c.MustGet("Subject").(entity.Subject)

传递的主题数据

但这行不通 =(,当我打印 obj 时,它只是一个空界面,我该怎么做?

我设法做了类似的事情!

我创建了以下中间件

var allowedTypes = []binding.Binding{
    binding.Query,
    binding.Form,
    binding.FormPost,
    binding.FormMultipart,
}

func Bind(name string, data interface{}, bindingType binding.Binding) gin.HandlerFunc {
    return func(ctx *gin.Context) {
        ok := false
        for _, b := range allowedTypes {
            if b == bindingType {
                ok = true
            }
        }
        if !ok {
            ctx.AbortWithError(
                http.StatusInternalServerError,
                fmt.Errorf("Bind function only allows %v\n", allowedTypes),
            )
        }
        _ = ctx.MustBindWith(data, bindingType)
        ctx.Set(name, data)
    }
}

记得在调用中传递一个指向你想要的类型的指针,像这样:

router.GET("/something", Bind("Object", &myObject, binding.Query))

我只限制了一些绑定类型,因为它们允许 ShouldBind 被多次调用,而 JSONXML 和其他绑定类型使用 Request 主体。

这样你可以传递多个 Bind 中间件,如果验证失败,它会自动中止 http.StatusBadRequest