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 时,标准库不会暴露已设置参数名的枚举能力。若这些值还需要被 BindPathValues、PathValuesBinder 或泛型路径参数 API 读取,请改用 binding.SetPathValue;它要求请求先经过 Middleware。
不使用 Middleware 仍可绑定 query、form 和已经由路由器匹配的路径参数,只是 query 不会复用缓存,且手动新增的路径参数名无法被枚举。
需要排除健康检查或静态资源等请求时,可使用 MiddlewareWithConfig 的 Skipper。返回 true 的请求会原样传递给下一个 handler,不会注入 query/path 缓存或 multipart 配置,因此也不能调用 binding.SetPathValue。
DefaultBinder 按顺序绑定:路径参数、查询参数(仅 GET、DELETE、HEAD 和 QUERY)、请求体。后一个来源可以覆盖前一个来源的字段。
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/xml 或 text/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/json、application/xml、text/xml、application/x-www-form-urlencoded 和 multipart/form-data。
不要直接向领域实体或授权模型绑定请求数据。请使用输入 DTO,再显式映射到业务对象,避免客户端覆盖诸如权限标志之类的不应由请求控制的字段。
Fluent API 适合从单一来源显式、强类型地读取参数:
var (
ids []int64
active bool
limit = int64(50)
)
err := binding.QueryParamsBinder(r).
Int64("limit", &limit).
Int64s("id", &ids).
Bool("active", &active).
BindError()可用的入口是 QueryParamsBinder、PathValuesBinder 和 FormFieldBinder。链条默认快速失败;使用 FailFast(false) 后以 BindErrors() 取得全部错误。每种基础类型提供 Type、MustType、Types 和 MustTypes 变体,也支持 BindWithDelimiter、BindUnmarshaler、encoding.TextUnmarshaler、json.Unmarshaler、time.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。
binding 不对外提供 Content-Type 判断函数。需要在绑定前显式识别请求类型时,使用 go-slim.dev/nego 的 TypeIs。它支持标准 MIME 类型、扩展名,以及 json、xml、form、text、protobuf、msgpack 等常用别名:
kind, err := nego.TypeIs(r.Header.Get(nego.HeaderContentType), "json", "xml")
if err != nil {
// Content-Type 格式无效。
}
if kind == "json" {
// Content-Type 是 application/json。
}没有匹配项时返回空字符串和 nil。BindBody 会在内部识别它支持的请求体类型,通常不需要调用方提前判断。
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。