前言
最近自己在搭 go-zero 的脚手架,遇到一个问题,原先的一个 post 请求是执行成功,当我添加了一个过滤器之后执行该请求就会报错请求体读取 eof 错误,我感到有点奇怪,为什么过滤器中的逻辑会影响到后续的请求呢?在此记录下这次的坑。
问题分析
先上结论,这是由于 go 中 http.request.body 请求体是一个流,它只能被读取一次,假如你在过滤器已经进行过流的读取,那么在后续的请求中就读取不到了。
示例的请求逻辑是这样,当请求到达后台时,先经过过滤器检查请求头中是否携带 token,否则则请求失败,成功则继续 post 请求逻辑,本次的 post 请求和过滤器只是做示例使用,逻辑很简单,过滤器中的逻辑就是去获取请求 header 中 token 字段,如果这个字段为空,则请求失败,代码示例如下:
func tokenfilter(next http.handlerfunc) http.handlerfunc {
return func(w http.responsewriter, r *http.request) {
type request struct {
token string `header:"token"`
}
var req request
err := httpx.parse(r, &req)
if err != nil || req.token == "" {
resp, _ := utils.tojsonstring(models.errorresponsewithcode(http.statusunauthorized, "token required"))
routers.httperror(w, http.statusunauthorized, resp)
return
}
next(w, r)
}
}
按照官方文档推荐,在过滤器我使用了 httpx.parse(r, &req) 获取请求体数据,导致后续 post 请求逻辑读取请求体失败,也就是说,这个方法中会消耗掉请求体流的数据,查看源码可以发现,这个方法是会读取请求中的 path,form,header,body 数据,赋值到我们的 struct 中:
func parse(r *http.request, v any) error {
kind := mapping.deref(reflect.typeof(v)).kind()
if kind != reflect.array && kind != reflect.slice {
if err := parsepath(r, v); err != nil {
return err
}
if err := parseform(r, v); err != nil {
return err
}
if err := parseheaders(r, v); err != nil {
return err
}
}
if err := parsejsonbody(r, v); err != nil {
return err
}
if valid, ok := v.(validation.validator); ok {
return valid.validate()
} else if val := validator.load(); val != nil {
return val.(validator).validate(r, v)
}
return nil
}
继续往下查看源码可以发现,其中的 parseform 方法和 parsejsonbody 方法就会消耗掉请求体流的数据,parsepath 方法 parseheaders 方法则不会,而在这个过滤器中,我们只需要获取 header 的数据,body 的数据需要由后续逻辑获取,所以在这里需要修改过滤只获取 header 数据的方法,如果需要的是 path 数据也是同样的道理,修改后的过滤器:
func tokenfilter(next http.handlerfunc) http.handlerfunc {
return func(w http.responsewriter, r *http.request) {
type request struct {
token string `header:"token"`
}
var req request
err := httpx.parseheaders(r, &req)
if err != nil || req.token == "" {
resp, _ := utils.tojsonstring(models.errorresponsewithcode(http.statusunauthorized, "token required"))
routers.httperror(w, http.statusunauthorized, resp)
return
}
next(w, r)
}
}
修改后后续节点即可正常获取请求体数据。
完整示例代码请参考:igolang
到此这篇关于go-zero读取请求体出现eof错误的解决方法的文章就介绍到这了,更多相关解决go-zero读取请求体出现eof错误内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论