dongsheng8664 2017-11-29 17:12
浏览 388

什么是允许多次读取http.Request.Body的正确方法

I want to access a http.Request's Body multiple times. The first time happens in my authentication middleware, it uses it to recreate a sha256 signature. The second time happens later, I parse it into JSON for use in my database.

I realize that you can't read from an io.Reader (or an io.ReadCloser in this case) more than once. I found an answer to another question with a solution:

When you first read the body, you have to store it so once you're done with it, you can set a new io.ReadCloser as the request body constructed from the original data. So when you advance in the chain, the next handler can read the same body.

Then in the example they set http.Request.Body to a new io.ReadCloser:

// And now set a new body, which will simulate the same data we read:
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))

Reading from Body and then setting a new io.ReadCloser at each step in my middleware seems expensive. Is this accurate?

In an effort to make this less tedious and expensive I use a solution described here to stash the parsed byte array in the Context() value of the request. Whenever I want it, its waiting for me already as byte array:

type bodyKey int
const bodyAsBytesKey bodyKey = 0

func newContextWithParsedBody(ctx context.Context, req *http.Request) context.Context {
    if req.Body == nil || req.ContentLength <= 0 {
        return ctx
    }

    if _, ok := ctx.Value(bodyAsBytesKey).([]byte); ok {
        return ctx
    }

    body, err := ioutil.ReadAll(req.Body)
    if err != nil {
        return ctx
    }

    return context.WithValue(ctx, bodyAsBytesKey, body)
}

func parsedBodyFromContext(ctx context.Context) []byte {
    if body, ok := ctx.Value(bodyAsBytesKey).([]byte); ok {
        return body
    }

    return nil
}

I feel like keeping a single byte array around is cheaper than reading a new one each time. Is this accurate? Are there pitfalls to this solution that I can't see?

  • 写回答

1条回答 默认 最新

  • duanmu2015 2017-11-29 17:20
    关注

    Is it "cheaper"? Probably, depending on what resource(s) you're looking at, but you should benchmark and compare your specific application to know for sure. Are there pitfalls? Everything has pitfalls, this doesn't seem particularly "risky" to me, though. Context values are kind of a lousy solution to any problem due to loss of compile-time type checking and the general increase in complexity and loss of readability. You'll have to decide what trade-offs to make in your particular situation.

    If you don't need the hash to be completed before the handler starts, you could also wrap the body reader in another reader (e.g. io.TeeReader), so that when you unmarshall the JSON, the wrapper can watch the bytes that are read and compute the signature hash. Is that "cheaper"? You'd have to benchmark & compare to know. Is it better? Depends entirely on your situation. It is an option worth considering.

    评论

报告相同问题?

悬赏问题

  • ¥15 winform的chart曲线生成时有凸起
  • ¥15 msix packaging tool打包问题
  • ¥15 finalshell节点的搭建代码和那个端口代码教程
  • ¥15 用hfss做微带贴片阵列天线的时候分析设置有问题
  • ¥15 Centos / PETSc / PETGEM
  • ¥15 centos7.9 IPv6端口telnet和端口监控问题
  • ¥20 完全没有学习过GAN,看了CSDN的一篇文章,里面有代码但是完全不知道如何操作
  • ¥15 使用ue5插件narrative时如何切换关卡也保存叙事任务记录
  • ¥20 海浪数据 南海地区海况数据,波浪数据
  • ¥20 软件测试决策法疑问求解答