GET、DELETE到底能不能带请求体😭

4,773 阅读3分钟

写在前面

今天在用gin写一个web项目的时候,突然遇到了一个问题。具体是这样的,前端传来一个delete请求,并且在body中携带token信息。那后端呢需要从中获取到token字段进行鉴权操作。

image.png 现在的问题就是后端不能在body体中拿到具体信息

网上找答案

在网上找了一通,居然找到点好玩的东西。 我之前一直认为,在http一些常用的请求方法GETPOSTPUTDELETEPATCH中只有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)

image.png

image.png 逐渐有点眉目了... 这时候我在网上又看到了点东西,

  1. 在浏览器中,直接发起GET请求,浏览器不会把该请求的body体数据给传到后端去,所以这就导致后端不可能在GET请求的body中能拿到数据了。
  2. 在浏览器中,直接发起DELETE请求,浏览器还是会把body体数据传到后端去

原来是浏览器在中间搞鬼啊😂😂😂

继续深挖

那既然这样,那我为什么我在gin启动的后端项目为什么拿不到数据呢?我先列出几个可能的原因

  1. 浏览器截断了请求,篡改了数据
  2. postman截断了请求,篡改了数据
  3. golanghttp包截断了请求,篡改了数据

上述三个原因的前两个不可能,我们从前面的实验代码中可知,那就只能是最后一个原因了。 话不多说,直接看源码......

看源码之前,我得先和大家说一下,在golanghttp包中,要想获取body体中的数据,有两种方式

  1. 自动:就是http包已经帮我们解析了请求体中的数据存放在PostForm中,不过在获取具体数据之前需要先执行http.Request.ParseForm()方法
  2. 手动:自己手动从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
}

通过源码,看到什么关键的信息了吗? 关键就在于第四行,他是直接判断,如果是POSTPUTPATCH三种请求方法,就帮我们自动解析请求体中的数据,其余一律不管,这就是导致我自动从请求体中拿不到数据的原因