Files
zogo/httpx/parse.go
T

76 lines
2.0 KiB
Go

package httpx
import (
"encoding/json"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)
// bindingValidator 与 gin 同源: 校验 struct 的 binding tag
var bindingValidator = validator.New(validator.WithRequiredStructEnabled())
func init() {
bindingValidator.SetTagName("binding")
// 关闭 gin 内置校验 (ShouldBindQuery/Header/JSON 内部各自 validate, 会使 json 字段的
// required 在 query 阶段误报)。校验统一由 Parse 末尾的 bindingValidator 收口。
binding.Validator = nil
}
// Parse 按统一顺序绑定请求参数: uri → query → header → json body。
// 各来源只做绑定不做校验, 全部绑定完后统一跑 binding 校验。
// (修复: gin 的 ShouldBindQuery 内部即执行校验, 会使 json 字段的 required 在 query 阶段误报)
func Parse(c *gin.Context, v any) error {
if err := parseUri(c, v); err != nil {
return err
}
if err := parseQuery(c, v); err != nil {
return err
}
if err := parseHeader(c, v); err != nil {
return err
}
if err := parseJSON(c, v); err != nil {
return err
}
return validate(c, v)
}
func parseUri(c *gin.Context, v any) error {
if _, ok := c.Params.Get("id"); ok {
return c.ShouldBindUri(v)
}
return nil
}
func parseQuery(c *gin.Context, v any) error {
return c.ShouldBindQuery(v)
}
func parseHeader(c *gin.Context, v any) error {
return c.ShouldBindHeader(v)
}
// parseJSON 直接解码 body, 不触发 gin 内部校验 (校验统一在 Parse 末尾)
func parseJSON(c *gin.Context, v any) error {
if c.Request == nil || c.Request.Body == nil || c.Request.ContentLength == 0 {
return nil
}
return json.NewDecoder(c.Request.Body).Decode(v)
}
// Validator 请求结构体可实现此接口做自定义校验, 在 binding 校验之前调用
type Validator interface {
Validate() error
}
func validate(c *gin.Context, v any) error {
if vv, ok := v.(Validator); ok {
if err := vv.Validate(); err != nil {
return err
}
}
return bindingValidator.Struct(v)
}