【问题标题】:net/http: http: ContentLength=222 with Body length 0net/http: http: ContentLength=222,正文长度为 0
【发布时间】:2015-07-10 09:57:07
【问题描述】:

如果出现连接/代理错误,我正在尝试重试请求。由于某些原因,我不断收到此错误,无论尝试重试请求,该错误似乎都无法恢复:

    Post https://m.somewebsite.co.uk/api/di/34433: http: ContentLength=222  with Body length 0

我做错了吗?我的第一个怀疑是 http.Request 以某种方式被消耗,所以在下一次尝试时它不再好。我应该管理副本吗?

func Post(URL string, form url.Values, cl *http.Client) ([]byte, error) {
    req, err := http.NewRequest("POST", URL, strings.NewReader(form.Encode()))
    if err != nil {
        log.Error(err)
        return nil, err
    }
    req.Header.Set("User-Agent", ua)
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    rsp, err := do(cl, req)

    if err != nil {
        return nil, err
    }
    defer rsp.Body.Close()
    b, err := ioutil.ReadAll(rsp.Body)
    if err != nil {
        log.Error(err)
        return nil, err
    }

    return b, nil
}

func do(cl *http.Client, req *http.Request)(*http.Response, error){
    rsp, err := cl.Do(req)
    for i := 0; IsErrProxy(err); i++ {
        log.Errorf("Proxy is slow or down ")
        time.Sleep(6 * time.Second)
5t      rsp, err = cl.Do(&ncp)
        if err == nil{
            return rsp, nil
        }
        if i > 10 {

            return nil, fmt.Errorf("after %v tries error: %v", i, err)
        }
    }
    return rsp, err
}

【问题讨论】:

    标签: go


    【解决方案1】:

    问题是请求正文在第一次调用 Do() 时被读取到末尾。在后续调用 Do() 时,不会从响应正文中读取任何数据。

    解决方法是将正文阅读器的创建移到 for 循环内。这要求在 for 循环中也创建请求。

    func Post(URL string, form url.Values, cl *http.Client) ([]byte, error) {
      body := form.Encode()
      for i := 0; i < 10; i++ {
        req, err := http.NewRequest("POST", URL, strings.NewReader(body))
        if err != nil {
            log.Error(err)
            return nil, err
        }
        req.Header.Set("User-Agent", ua)
        req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
        rsp, err := cl.Do(req)
        if err == nil {
            defer rsp.Body.Close()
            b, err := ioutil.ReadAll(rsp.Body)
            if err != nil {
                log.Error(err)
                return nil, err
            }
            return b, nil
        }
    
        if !IsErrorProxy(err) {
            return nil, err
        }
    
        log.Errorf("Proxy is slow or down ")
        time.Sleep(6 * time.Second)
      }
      return nil, fmt.Errorf("after 10 tries error: %v", err)
    }
    

    【讨论】:

      【解决方案2】:

      让我们看看http.NewRequest做了什么

      rc, ok := body.(io.ReadCloser)
      if !ok && body != nil {
          rc = ioutil.NopCloser(body)
      }
      // The host's colon:port should be normalized. See Issue 14836.
      u.Host = removeEmptyPort(u.Host)
      req := &Request{
          Method:     method,
          URL:        u,
          Proto:      "HTTP/1.1",
          ProtoMajor: 1,
          ProtoMinor: 1,
          Header:     make(Header),
          Body:       rc,
          Host:       u.Host,
      }
      

      bodyio.Reader 的类型,通过ioutil.NopCloser 转换为io.ReaderCloser。正如@Cerise Limón 所说,Request.Body 已被读取并且流更接近,所以当您再次Do() 时,Body Length 为0。

      所以,我们可以在调用 Do 之前重置 Request.Body。

      func Post(URL string, form url.Values, cl *http.Client) ([]byte, error) {
          requestBodyString := form.Encode()
          req, err := http.NewRequest("POST", URL, strings.NewReader(requestBodyString))
          // ...
      
          rsp, err := do(cl, req, requestBodyString)
      
          //...
          return b, nil
      }
      
      func do(cl *http.Client, req *http.Request, requestBodyString string)(*http.Response, error){
          rsp, err := cl.Do(req)
          for i := 0; IsErrProxy(err); i++ {
              log.Errorf("Proxy is slow or down ")
              time.Sleep(6 * time.Second)
              // reset Request.Body
              req.Body = ioutil.NopCloser(strings.NewReader(requestBodyString))
              rsp, err = cl.Do(&req)
              if err == nil{
                  return rsp, nil
              }
              if i > 10 {
      
                  return nil, fmt.Errorf("after %v tries error: %v", i, err)
              }
          }
          return rsp, err
      }
      

      【讨论】:

        【解决方案3】:

        我找到了一种无需每次都重新创建请求的方法。这是一个示例代码,它是对@Cerise Limón 的一个非常小的修改,与@Rambo 的代码相似,它只创建一次请求:

        func Post(URL string, data *bytes.Buffer, cl *http.Client) ([]byte, error) {
            var err error
        
            req, err := http.NewRequest("POST", URL, ioutil.NopCloser(data))
            if err != nil {
                log.Errorf("Unable to create the request: %v", err)
                return nil, err
            }
            req.Header.Set("User-Agent", ua)
        
            for i := 0; i < 10; i++ {
                rsp, err := cl.Do(req)
                if err == nil && rsp.StatusCode == 200 {
                    defer rsp.Body.Close()
                    b, err := ioutil.ReadAll(rsp.Body)
                    if err != nil {
                        fmt.Printf("Error: %v", err)
                        return nil, err
                    }
                    return b, nil
                }
        
                log.Errorf("Proxy is slow or down %v", err)
                time.Sleep(1 * time.Second)
            }
            return nil, fmt.Errorf("after 10 tries error: %v", err)
        }
        
        func main() {
            client := http.Client{}
            data := []byte{0, 1, 2, 3, 4}
            Post("http://server/my/api/resource/", bytes.NewBuffer(data), &client)
        }
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-10
          • 1970-01-01
          • 2010-10-17
          • 1970-01-01
          • 2020-12-12
          相关资源
          最近更新 更多