【问题标题】:Content Length in GolangGolang 中的内容长度
【发布时间】:2018-11-20 22:32:07
【问题描述】:

我在网上找不到任何有用的东西。

我正在编写一个 REST API,我想记录请求正文的大小(以字节为单位)作为指标。 Go net/http API 不直接提供。 http.Request 确实有 Content-Length 字段,但该字段可以为空,否则客户端可能会发送虚假数据。

有没有办法在中间件级别获得它?蛮力方法是阅读全文并检查大小。但是如果我在中间件中这样做,处理程序将无法访问主体,因为它会被读取并关闭。

【问题讨论】:

  • 正如你所说,它仍然是“蛮力”,但你可以使用中间件来读取它,检查大小,然后使用新的 io.ReadCloser 将其替换回 Request.Body,因为那样是Body 的全部。 golang.org/pkg/net/http/#Request

标签: http go


【解决方案1】:

你为什么要在这里找个中间人?
简单的方法是b, err = io.Copy(anyWriterOrMultiwriter, r.Body)
berr == nil时请求的总内容长度
根据需要使用请求正文。还有b, err = io.Copy(ioutil.Discard, r.Body)

【讨论】:

    【解决方案2】:

    您可以编写一个自定义的ReadCloser,它代理现有的并计算字节数。比如:

    type LengthReader struct {
        Source io.ReadCloser
        Length int
    }
    
    func (r *LengthReader) Read(b []byte) (int, error) {
        n, err := r.Source.Read(b)
        r.Length += n
        return n, err
    }
    
    func (r *LengthReader) Close() error {
        var buf [32]byte
        var n int
        var err error
        for err == nil {
            n, err = r.Source.Read(buf[:])
            r.Length += n
        }
        closeerr := r.Source.Close()
        if err != nil && err != io.EOF {
            return err
        }
        return closeerr
    }
    

    这将在您从流中读取字节数时对其进行计数,并且在关闭时它将首先消耗并计算所有剩余的未读字节数。完成流后,您可以访问长度。

    【讨论】:

    • 太棒了!这几乎让人觉得奇怪,这已经不是内置的东西了。在这里,我试图弄清楚如何使用中间件来避免复制。
    • 这确实是一种聪明的做法。唯一的问题(我没有在问题中提到这一点)是我不知道请求的大小,而不是在读取之后。
    • @Husain Fair。尽管在一般情况下,如果不先读取请求就不可能获得请求的大小,而使用另一个答案,您只需预先读取整个请求并将其存储以供以后重新读取。
    【解决方案3】:

    选项 1

    使用TeeReader,这是可扩展的。它将阅读器分成两部分,其中一个使用分配的内存计算大小。另外,在第一种情况下

      maxmem := 4096
      var buf bytes.Buffer
      // comment this line out if you want to disable gathering metrics
      resp.Body = io.TeeReader(resp.Body, &buf) 
    
      readsize := func(r io.Reader) int {
        bytes := make([]byte, maxmem)
        var size int
          for {
            read, err := r.Read(bytes)
            if err == io.EOF {
            break
          }
          size += read
        }
        return size
      }
    
      log.Printf("Size is %d", readsize(&buf))
    

    选项2不可扩展的方式(原始答案)

    你可以只读取正文,计算大小,然后解组为结构,使其变为:

        b, _ := ioutil.ReadAll(r.Body)
    
        size := len(b) // can be nil so check err in your app
        
        if err := json.Unmarshal(b, &input); err != nil {
            s.BadReq(w, errors.New("error reading body"))
            return
        }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-05
      • 1970-01-01
      • 2013-05-28
      • 2012-11-03
      • 1970-01-01
      • 1970-01-01
      • 2012-05-01
      • 1970-01-01
      相关资源
      最近更新 更多