什么是允许多次读取http.Request.Body的正确方法 [英] What is the proper way to allow multiple reads of http.Request.Body

查看:1330
本文介绍了什么是允许多次读取http.Request.Body的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想多次访问 http.Request Body 。第一次发生在我的身份验证中间件中,它使用它来重新创建sha256签名。第二次发生的时候,我把它解析成JSON用在我的数据库中。



我意识到你不能读取 io。 Reader (或者 io.ReadCloser 在这种情况下)不止一次。我找到了对另一个问题的回答,并提供了一个解决方案:


当您第一次阅读正文时,您必须将其存储完毕,您可以将新的 io.ReadCloser 设置为请求体由原始数据构造而成。因此,当您在链中前进时,下一个处理程序可以读取同一个主体。

然后在示例中,它们将 http.Request.Body 添加到新的 io.ReadCloser



<$ p $
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
现在设置一个新的主体,它将模拟我们读取的相同数据: / code>

读取 Body ,然后设置一个新的 io.ReadCloser 在我的中间件中的每一步似乎都很昂贵。这是准确的吗?



为了减少这种繁琐而昂贵的工作,我使用这里描述的解决方案,以便将解析的字节数组存储在 Context()请求的值。每当我需要它,它已经以字节数组的形式等待我:

  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
}

返回context.WithValue(ctx,bodyAsBytesKey,body)
}

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

return nil
}

我觉得保持一个字节数组比每次读一个新数组要便宜。这是否准确?是否有这种解决方案的缺陷,我看不到?

解决方案

它是便宜吗?可能取决于您正在查看的资源,但您应该对您的特定应用程序进行基准测试并进行比较以确定。有缺陷吗?一切都有缺陷,但这对我来说并不特别危险。由于编译时类型检查的丢失以及复杂性和可读性的普遍增加,上下文值对于任何问题都是糟糕的解决方案。您必须决定在您的特定情况下做出什么样的折衷。



如果您不需要在处理程序启动之前完成哈希,您可以还要在另一个阅读器中包装正文阅读器(例如 io.TeeReader ),以便在解组JSON时,封装器可以观察读取的字节并计算签名哈希。那是便宜吗?你必须进行基准测试&比较知道。好点吗?完全取决于你的情况。这是一个值得考虑的选择。


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?

解决方案

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.

这篇关于什么是允许多次读取http.Request.Body的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆