记录大作业项目中遇到两个的问题 |青训营笔记

357 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第五篇笔记

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的数据缓存了下来,保存了requestsBody到上下文,允许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)
}