这是我参与「第三届青训营 -后端场」笔记创作活动的第五篇笔记
gin 统一参数绑定时 EOF
在大作业项目开发过程中,使用了在 《Go 编程之旅》 这本书中介绍的统一参数绑定和校验的方法。这个方法中定义了使用ShouldBind 绑定,并封装 validator 参数校验错误类型,对错误消息进行进行国际化处理。
func BindAndValid(c *gin.Context, v interface{}) (bool, error) {
var errs ValidErrors
err := c.ShouldBind(v)
if err != nil {
v := c.Value("trans")
trans, _ := v.(ut.Translator)
verrs, ok := err.(val.ValidationErrors)
if !ok {
return false, err
}
for key, value := range verrs.Translate(trans) {
errs = append(errs, &ValidError{
Key: key,
Message: value,
})
}
return false, errs
}
return true, nil
}
使用接口测试工具发送请求时,参数绑定没有出现问题。但是当对接上前端,进行跨域访问时,ShouldBind 方法会报 io.EOF 错误,我们查阅了有关的文档和博客,发现在源码中的 readLocked 方法,在第一次访问后就将 b.sawEOF 设置为 true,第二次取 buffer 时,当 *body 中的这个标记为 True时,就会返回 io.EOF 的异常
也就是说应该是在同一个上下文中出现了重复调用绑定方法的现象,查阅文档后,发现官方提供了一个 ShouldBindBodyWith 的方法,可以支持重复绑定,原理就是将body的数据缓存了下来,保存了requests的Body到上下文,允许Body被继续调用。但是因为会先读取 Body 再绑定,在绑定一次的场景下,使用 ShouldBind 系列就足够了
第二次调用时,利用这个方法替换就能解决问题了
var errs ValidErrors
err := c.ShouldBind(v)
//绑定错误时用 ShouldBindWith 替换
if err == io.EOF {
err = c.ShouldBindWith(v, binding.Query)
}
if err != nil {
日期类型 JSON 序列化
前端有个生日类型的字段需要传递,但格式模式为国际标准 RFC3339 ,无法被 UnmarshalJSON 正常解析
解决方法是定义了的时间类型的类型别名,然后重新自定义了序列化方法,按照指定格式序列化
type JsonTime time.Time
const (
timeFormat = "2006-01-02"
)
func (t *JsonTime) UnmarshalJSON(data []byte) (err error) {
now, err := time.ParseInLocation(`"`+timeFormat+`"`, string(data), time.Local)
*t = JsonTime(now)
return
}
func (t JsonTime) MarshalJSON() ([]byte, error) {
b := make([]byte, 0, len(timeFormat)+2)
b = append(b, '"')
b = time.Time(t).AppendFormat(b, timeFormat)
b = append(b, '"')
return b, nil
}
func (t JsonTime) String() string {
return time.Time(t).Format(timeFormat)
}