写在前面
今天在用gin写一个web项目的时候,突然遇到了一个问题。具体是这样的,前端传来一个delete请求,并且在body中携带token信息。那后端呢需要从中获取到token字段进行鉴权操作。
现在的问题就是后端不能在
body体中拿到具体信息
网上找答案
在网上找了一通,居然找到点好玩的东西。
我之前一直认为,在http一些常用的请求方法GET、POST、PUT、DELETE、PATCH中只有GET请求的body体是不能传数据的,具体来说是给了后端也不会接收的,但其实在http协议中,上述的请求方法的请求体都可传给后端,后端也是可以接收到的。
直接上代码:
const http = require("http")
http.createServer((req, res)=>{
const {url, method} = req
console.log(`URL\t\t${url}`)
console.log(`METHOD\t\t${method}`)
const body = []
req.on("data", (chunk) =>{
body.push(chunk)
}).on("end", ()=>{
let bodyString = Buffer.concat(body).toString()
console.log(`BODY\t\t${bodyString}`)
res.end(bodyString||"body is empty")
})
}).listen(3000)
逐渐有点眉目了...
这时候我在网上又看到了点东西,
- 在浏览器中,直接发起
GET请求,浏览器不会把该请求的body体数据给传到后端去,所以这就导致后端不可能在GET请求的body中能拿到数据了。 - 在浏览器中,直接发起
DELETE请求,浏览器还是会把body体数据传到后端去
原来是浏览器在中间搞鬼啊😂😂😂
继续深挖
那既然这样,那我为什么我在gin启动的后端项目为什么拿不到数据呢?我先列出几个可能的原因
- 浏览器截断了请求,篡改了数据
- postman截断了请求,篡改了数据
golang的http包截断了请求,篡改了数据
上述三个原因的前两个不可能,我们从前面的实验代码中可知,那就只能是最后一个原因了。 话不多说,直接看源码......
看源码之前,我得先和大家说一下,在golang的http包中,要想获取body体中的数据,有两种方式
- 自动:就是
http包已经帮我们解析了请求体中的数据存放在PostForm中,不过在获取具体数据之前需要先执行http.Request.ParseForm()方法 - 手动:自己手动从
http.Request.Body中解析数据。麻烦...
既然这样,我们就定位到了需要看的源码的位置:http.Request.ParseForm()的具体实现
func (r *Request) ParseForm() error {
var err error
if r.PostForm == nil {
if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" {
r.PostForm, err = parsePostForm(r)
}
if r.PostForm == nil {
r.PostForm = make(url.Values)
}
}
if r.Form == nil {
if len(r.PostForm) > 0 {
r.Form = make(url.Values)
copyValues(r.Form, r.PostForm)
}
var newValues url.Values
if r.URL != nil {
var e error
newValues, e = url.ParseQuery(r.URL.RawQuery)
if err == nil {
err = e
}
}
if newValues == nil {
newValues = make(url.Values)
}
if r.Form == nil {
r.Form = newValues
} else {
copyValues(r.Form, newValues)
}
}
return err
}
通过源码,看到什么关键的信息了吗?
关键就在于第四行,他是直接判断,如果是POST、PUT、PATCH三种请求方法,就帮我们自动解析请求体中的数据,其余一律不管,这就是导致我自动从请求体中拿不到数据的原因