未为具有空/零负载的 PATCH 请求设置 Content-Length 标头 - GoLang

问题描述 投票:0回答:2

我观察到,没有为空/零负载的 PATCH 请求设置 Content-Length 标头。即使我们通过

req.Header.Set("content-length", "0")
手动设置它,它实际上并没有在发出的请求中设置。 这种奇怪的行为(Go bug?)仅发生在 PATCH 请求中,并且仅当有效负载为空或 nil(或设置为 http.NoBody)时

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
)

func main() {

    url := "http://localhost:9999"
    method := "PATCH"

    payload := strings.NewReader("")
    client := &http.Client {
    }
    req, err := http.NewRequest(method, url, payload)

    if err != nil {
        fmt.Println(err)
    }
    req.Header.Set("Authorization", "Bearer my-token")
    req.Header.Set("Content-Length", "0") //this is not honoured

    res, err := client.Do(req)
    defer res.Body.Close()
    body, err := ioutil.ReadAll(res.Body)

    fmt.Println(string(body))
}

即使在最新的 go 版本中也可以重现

1.15
。 只需针对一个简单的 http 服务器运行上面的代码并亲自查看即可。

是否有任何解决方案/解决方法可以发送 Content-Length 设置为 0 的 PATCH 请求?

http go content-length http-content-length
2个回答
1
投票

您可以通过将 TransferEncoding 设置为

Content-Length
来告诉 HTTP 客户端包含值为 0 的
identity
标头,如下所示:

  url := "http://localhost:9999"
  method := "PATCH"
  
  client := &http.Client{}
  req, err := http.NewRequest(method, url, http.NoBody)
  if err != nil {
    panic(err)
  } 

  req.TransferEncoding = []string{"identity"} 
  req.Header.Set("Authorization", "Bearer my-token")
  //  req.Header.Set("Content-Length", "0")

请注意对原始代码的以下更改:

  • 重要的
    req.TransferEncoding = []string{"identity"}
  • 指定空主体的惯用方式:
    http.NoBody
    (对发送长度没有影响)
  • 注释掉了
    req.Header.Set("Content-Length", "0")
    ,客户端自行填写
  • 还更改为出现错误时恐慌,您可能不想继续

identity
的传输编码没有写入请求,因此除了标头
Content-Length = 0
之外,请求看起来与之前相同。

不幸的是,这没有记录(请随时向 Go 团队提出问题),但可以在以下代码中看到:

繁琐的细节

transferWriter.writeHeader 检查以下内容以写入

Content-Length
标头:

    // Write Content-Length and/or Transfer-Encoding whose values are a
    // function of the sanitized field triple (Body, ContentLength,
    // TransferEncoding)
    if t.shouldSendContentLength() {
        if _, err := io.WriteString(w, "Content-Length: "); err != nil {
            return err
        }
        if _, err := io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n"); err != nil {
            return err
        }

反过来,shouldCheckContentLength 在长度为零的情况下查看传输编码:

    if t.ContentLength == 0 && isIdentity(t.TransferEncoding) {
        if t.Method == "GET" || t.Method == "HEAD" {
            return false
        }
        return true
    }

isIdentity 验证

TransferEncoding
完全
[]string{"identity"}
:

func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" }) 

0
投票

由于有效负载是一个 io.Reader,golang 没有任何方法可以预先知道内容的长度是多少。 你需要告诉 go 内容的长度是多少。

req, err := http.NewRequest(method, url, payload)
if err != nil {
    fmt.Println(err)
}    
req.ContentLength = len(payload)
© www.soinside.com 2019 - 2024. All rights reserved.