Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

binding

English

go-slim.dev/binding 为 HTTP handler 提供独立的输入绑定能力。它以 *http.Request 为唯一入口,将路径参数、查询参数、请求头和请求体解析为 DTO 字段,或显式转换为强类型变量。

它不包含路由、响应渲染、校验器或 HTTP 错误处理;这些职责由使用它的框架或应用决定。

安装

go get go-slim.dev/binding

本模块当前使用 Go 1.25.5,具体版本以 go.mod 为准。

请求初始化

在应用入口处用 Middleware 包裹路由器:它会缓存 URL.Query(),并为路径参数枚举和 multipart 表单解析准备请求级配置。

mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
	var input struct {
		ID   uint   `param:"id"`
		Page int    `query:"page"`
		Name string `json:"name"`
	}

	if err := new(binding.DefaultBinder).Bind(r, &input); err != nil {
		// 由应用根据 errors.Is(err, binding.ErrBadRequest) 决定响应。
		return
	}
})

server := &http.Server{
	Addr:    ":8080",
	Handler: binding.Middleware(mux),
}

路由器直接调用 req.SetPathValue 时,标准库不会暴露已设置参数名的枚举能力。若这些值还需要被 BindPathValuesPathValuesBinder 或泛型路径参数 API 读取,请改用 binding.SetPathValue;它要求请求先经过 Middleware

不使用 Middleware 仍可绑定 query、form 和已经由路由器匹配的路径参数,只是 query 不会复用缓存,且手动新增的路径参数名无法被枚举。

需要排除健康检查或静态资源等请求时,可使用 MiddlewareWithConfigSkipper。返回 true 的请求会原样传递给下一个 handler,不会注入 query/path 缓存或 multipart 配置,因此也不能调用 binding.SetPathValue

结构体绑定

DefaultBinder 按顺序绑定:路径参数、查询参数(仅 GETDELETEHEADQUERY)、请求体。后一个来源可以覆盖前一个来源的字段。

type CreateUserInput struct {
	ID       uint     `param:"id"`
	Page     int      `query:"page"`
	Tags     []string `query:"tag"`
	RequestID string   `header:"X-Request-ID"`
	Name     string   `form:"name" json:"name" xml:"name"`
}

var input CreateUserInput
if err := new(binding.DefaultBinder).Bind(r, &input); err != nil {
	// 处理输入错误。
}

支持的来源标签:

标签 来源
param 路径参数
query 查询参数
header 请求头;不会由 DefaultBinder.Bind 自动绑定
form 表单数据;非 multipart 表单包含 query 与 body
json application/json 请求体,使用 encoding/json
xml application/xmltext/xml 请求体

重复的 query、header 或 form 值会绑定到切片字段。路径、query、header 和 form 字段必须显式声明标签;JSON 和 XML 的字段命名遵循标准库行为。

若只需要一个来源,可直接调用:

err := binding.BindPathValues(r, &input)
err = binding.BindQueryParams(r, &input)
err = binding.BindHeaders(r, &input)
err = binding.BindBody(r, &input)

请求体支持 application/jsonapplication/xmltext/xmlapplication/x-www-form-urlencodedmultipart/form-data

不要直接向领域实体或授权模型绑定请求数据。请使用输入 DTO,再显式映射到业务对象,避免客户端覆盖诸如权限标志之类的不应由请求控制的字段。

Fluent 绑定

Fluent API 适合从单一来源显式、强类型地读取参数:

var (
	ids    []int64
	active bool
	limit  = int64(50)
)

err := binding.QueryParamsBinder(r).
	Int64("limit", &limit).
	Int64s("id", &ids).
	Bool("active", &active).
	BindError()

可用的入口是 QueryParamsBinderPathValuesBinderFormFieldBinder。链条默认快速失败;使用 FailFast(false) 后以 BindErrors() 取得全部错误。每种基础类型提供 TypeMustTypeTypesMustTypes 变体,也支持 BindWithDelimiterBindUnmarshalerencoding.TextUnmarshalerjson.Unmarshalertime.Time 和 Unix 时间戳。

也可使用泛型 API:

id, err := binding.PathParam[uint](r, "id")
page, err := binding.QueryParamOr(r, "page", 1)
tags, err := binding.FormValues[string](r, "tag")

缺少必选的泛型参数会返回 ErrNonExistentKey;转换失败会返回 *FieldError

Content-Type 辅助函数

binding 不对外提供 Content-Type 判断函数。需要在绑定前显式识别请求类型时,使用 go-slim.dev/negoTypeIs。它支持标准 MIME 类型、扩展名,以及 jsonxmlformtextprotobufmsgpack 等常用别名:

kind, err := nego.TypeIs(r.Header.Get(nego.HeaderContentType), "json", "xml")
if err != nil {
	// Content-Type 格式无效。
}
if kind == "json" {
	// Content-Type 是 application/json。
}

没有匹配项时返回空字符串和 nilBindBody 会在内部识别它支持的请求体类型,通常不需要调用方提前判断。

错误分类

binding 只标记输入错误,不决定 HTTP 状态码或响应格式。外层通过 errors.Is 分类,并可通过 errors.As 读取字段错误详情:

switch {
case errors.Is(err, binding.ErrUnsupportedMediaType):
	// 例如:映射为 415
case errors.Is(err, binding.ErrBadRequest):
	// 例如:映射为 400
}

var fieldErr *binding.FieldError
if errors.As(err, &fieldErr) {
	// fieldErr.Field、fieldErr.Message 和 fieldErr.Values
}

来源与许可

本项目的绑定实现源自并改造自 Echo 的绑定设计。详细来源说明见 NOTICE,许可条款见 LICENSE

About

为 HTTP handler 提供独立的输入绑定能力

Resources

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages