Gin 怎么给自定义类型实现 encoding.TextUnmarshaler 并通过 parser 标签参与绑定
Gin 怎么给自定义类型实现 encoding.TextUnmarshaler 并通过 parser 标签参与绑定【免费下载链接】ginGin is a high-performance HTTP web framework written in Go. It provides a Martini-like API but with significantly better performance—up to 40 times faster—thanks to httprouter. Gin is designed for building REST APIs, web applications, and microservices.项目地址: https://gitcode.com/GitHub_Trending/gi/gin在 Gin 项目里写接口时常见一种参数客户端传的是字符串比如2000-01-01这样的日期、十六进制值、自定义 ID但结构体字段希望存放转换后的自定义类型值。Gin 的form/uri默认绑定只处理内置的常见类型要让自定义类型用自己的解析逻辑参与绑定做法是给该类型实现标准库的encoding.TextUnmarshaler接口并在字段标签上加parserencoding.TextUnmarshaler。本文基于 docs/doc.md 中 Bind custom unmarshaler 一节的完整示例给出从实现接口到用 curl 验证的全过程。准备条件一个已引入github.com/gin-gonic/gin的 Go 项目接口通过form或uri标签绑定参数示例中使用ctx.BindQueryGET 查询参数走form标签。第一步给自定义类型实现 UnmarshalText以文档中的Birthday类型为例把请求值里的-替换成/package main import ( encoding strings github.com/gin-gonic/gin ) type Birthday string func (b *Birthday) UnmarshalText(text []byte) error { *b Birthday(strings.Replace(string(text), -, /, -1)) return nil } var _ encoding.TextUnmarshaler (*Birthday)(nil) //assert Birthday implements encoding.TextUnmarshaler几个必须注意的点签名固定为UnmarshalText(text []byte) error且示例用的是指针接收者——gin 在绑定时会把字段的地址作为encoding.TextUnmarshaler来断言最后一行var _ encoding.TextUnmarshaler (*Birthday)(nil)是编译期断言类型没实现接口时直接编译报错不必等运行时才发现标签不生效请求里的原始值例如2000-01-01会作为text传进来转换逻辑包括需要时的校验都写在UnmarshalText内。第二步在字段标签里加 parser 选项类型实现了接口还不够gin 只在标签声明了 parser 时才会调用UnmarshalTextvar request struct { Birthday Birthday form:birthday,parserencoding.TextUnmarshaler Birthdays []Birthday form:birthdays,parserencoding.TextUnmarshaler collection_format:csv BirthdaysDefault []Birthday form:birthdaysDef,default2020-09-01;2020-09-02,parserencoding.TextUnmarshaler collection_format:csv }parserencoding.TextUnmarshaler写在form或uri标签的选项部分uri绑定用法相同例如uri:field,parserencoding.TextUnmarshaler客户端未传该字段时可用default提供默认值默认值同样会走UnmarshalText数组默认值用分号;分隔逗号在标签里是选项分隔符不能出现在默认值中配合collection_format:csv时客户端用逗号分隔传多个值如birthdays2000-01-01,2000-01-02。完整示例与验证下面的完整代码来自 docs/doc.md监听:8088/test接口把绑定结果直接以 JSON 返回package main import ( encoding strings github.com/gin-gonic/gin ) type Birthday string func (b *Birthday) UnmarshalText(text []byte) error { *b Birthday(strings.Replace(string(text), -, /, -1)) return nil } var _ encoding.TextUnmarshaler (*Birthday)(nil) //assert Birthday implements encoding.TextUnmarshaler func main() { route : gin.Default() var request struct { Birthday Birthday form:birthday,parserencoding.TextUnmarshaler Birthdays []Birthday form:birthdays,parserencoding.TextUnmarshaler collection_format:csv BirthdaysDefault []Birthday form:birthdaysDef,default2020-09-01;2020-09-02,parserencoding.TextUnmarshaler collection_format:csv } route.GET(/test, func(ctx *gin.Context) { _ ctx.BindQuery(request) ctx.JSON(200, request) }) _ route.Run(:8088) }说明一点文档示例把request声明在 handler 外面所有请求会复用同一个结构体实例实际接口里建议把结构体声明在 handler 内部每个请求独立绑定。启动后按文档给出的方式验证curl localhost:8088/test?birthday2000-01-01birthdays2000-01-01,2000-01-02文档展示的示例结果是{Birthday:2000/01/01,Birthdays:[2000/01/01,2000/01/02],BirthdaysDefault:[2020/09/01,2020/09/02]}核对要点Birthday和Birthdays的值都被UnmarshalText转成了/分隔形式说明 parser 标签生效BirthdaysDefault没有被请求参数覆盖但默认值2020-09-01;2020-09-02同样经过了UnmarshalText。如果响应里还是-形式说明标签没写对或类型没有实现接口此时 gin 会静默回落到默认绑定见下文。规则与边界文档和 binding/form_mapping.go 中的trySetUsingParser实现给出了这些明确行为类型没有实现encoding.TextUnmarshaler却写了parserencoding.TextUnmarshaler时gin 忽略该选项继续用默认绑定逻辑——不会报错排查时容易误以为接口没生效UnmarshalText返回 error 时gin 停止绑定并把错误返回给客户端因此可以在UnmarshalText里做参数格式校验例如 binding/form_mapping_test.go 中customUnmarshalTextType对不符合a:b:c格式的值返回invalid format反过来即使类型实现了UnmarshalText标签里不写parserencoding.TextUnmarshaler时gin 仍然走默认绑定。测试里对十六进制类型hex11不带 parser 的字段按默认 int 绑定得到十进制 11带 parser 的字段得到0x11即 17。这是为保持向后兼容而设计的也是实现了接口但转换没生效时最该先检查的地方parser 选项只作用于form/uri绑定。可选替代路径gin 的 BindUnmarshaler 接口如果类型已经实现了encoding.TextUnmarshaler但你想换一种 gin 绑定该类型的方式文档给出的例子是想改变返回的错误信息可以改用 gin 提供的binding.BindUnmarshaler接口它的方法签名是UnmarshalParam(param string) errorpackage main import ( strings github.com/gin-gonic/gin github.com/gin-gonic/gin/binding ) type Birthday string func (b *Birthday) UnmarshalParam(param string) error { *b Birthday(strings.Replace(param, -, /, -1)) return nil } var _ binding.BindUnmarshaler (*Birthday)(nil) //assert Birthday implements binding.BindUnmarshaler两个接口的优先级规则类型同时实现encoding.TextUnmarshaler和BindUnmarshaler时gin 默认使用BindUnmarshaler只有标签里显式写了parserencoding.TextUnmarshaler才会调用UnmarshalTextUnmarshalParam返回 error 时gin 同样停止绑定并把错误返回给客户端走BindUnmarshaler时字段标签不需要parser选项验证方式与上面相同curl localhost:8088/test?birthday2000-01-01birthdays2000-01-01,2000-01-02文档展示的示例结果与 TextUnmarshaler 版本一致。两条路径覆盖同一场景需要自定义解析就用parserencoding.TextUnmarshalerUnmarshalText需要定制 gin 的绑定行为尤其是错误信息用UnmarshalParam并按上面的优先级规则决定是否加 parser 标签。【免费下载链接】ginGin is a high-performance HTTP web framework written in Go. It provides a Martini-like API but with significantly better performance—up to 40 times faster—thanks to httprouter. Gin is designed for building REST APIs, web applications, and microservices.项目地址: https://gitcode.com/GitHub_Trending/gi/gin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考