我想多次访问http.Request
个Body
。第一次发生在我的身份验证中间件中,它使用它来重新创建sha256签名。第二次发生,我将其解析为JSON以便在我的数据库中使用。
我意识到您不能多次从io.Reader
(或本例中的io.ReadCloser
)读取。我发现answer to another question有一个解决方案:
当您第一次阅读正文时,您必须将其存储,所以一旦完成它,您可以设置一个新的
io.ReadCloser
作为从原始数据构造的请求正文。因此,当您在链中前进时,下一个处理程序可以读取相同的主体。
然后在示例中,他们将http.Request.Body
设置为新的io.ReadCloser
:
// And now set a new body, which will simulate the same data we read:
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
从Body
读取,然后在我的中间件的每一步设置一个新的io.ReadCloser
似乎很昂贵。这准确吗?
为了减少繁琐和昂贵,我使用a solution described here将已解析的字节数组存储在请求的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
}
return context.WithValue(ctx, bodyAsBytesKey, body)
}
func parsedBodyFromContext(ctx context.Context) []byte {
if body, ok := ctx.Value(bodyAsBytesKey).([]byte); ok {
return body
}
return nil
}
我觉得保持一个单字节数组比每次读一个新数组便宜。这准确吗?这个解决方案是否存在我无法看到的陷阱?
答案 0 :(得分:0)
它“更便宜”吗?可能,取决于您正在查看的资源,但您应该对您的特定应用程序进行基准测试和比较以确定。有陷阱吗?一切都有陷阱,但这对我来说似乎并不特别“冒险”。由于编译时类型检查的丢失以及复杂性的普遍增加和可读性的丧失,上下文值对于任何问题都是一种糟糕的解决方案。你必须决定在你的特定情况下做出什么样的权衡。
如果你不需要在处理程序启动之前完成哈希,你也可以将正文阅读器包装在另一个阅读器中(例如io.TeeReader
),这样当你解组JSON时,包装器就可以看了读取的字节数并计算签名哈希值。那“更便宜”吗?你需要基准和&amp;比较知道。好点吗?完全取决于您的情况。这是一个值得考虑的选择。