Golang阅读请求正文 [英] Golang read request body

查看:129
本文介绍了Golang阅读请求正文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写我自己的logginMiddleware。基本上,我需要记录请求的正文和响应。我面临的问题是,当我阅读身体时,它变得空虚,我不能读它两次。
我明白这是因为它是ReadCloser类型。有没有办法让身体回到最初?

解决方案

检查和嘲讽请求正文



正文,您必须将其存储完毕后,您可以设置新的 io.ReadCloser 作为从原始数据构建的请求主体。所以当你在链中前进时,下一个处理程序可以读取同一个主体。



一个选项是使用 ioutil.ReadAll() ,它为您提供了一个字节切片。



您可以使用 bytes.NewBuffer() 来获取 <$ c

最后一个缺失的部分是使 > io.Reader an io.ReadCloser ,因为 bytes.Buffer 没有 Close()方法。为此,您可以使用 ioutil.NopCloser() ,它包装了一个 io.Reader ,并返回一个 io.ReadCloser ,其中添加了 Close()方法将是一个无操作(不做任何事情)。



请注意,您甚至可以修改你用来创建新主体的字节片。您可以完全控制它。



虽然可能有其他HTTP字段,如内容长度和校验和,但如果您只修改数据。如果后续处理程序检查这些内容,您还需要修改这些内容!



检查/修改响应正文



如果您还想阅读响应正文,则必须将 http.ResponseWriter ,然后传递链上的包装器。这个包装器可以缓存发出的数据,你可以在运行之后(随着后续的处理程序的写入)检查数据。



这是一个简单的 ResponseWriter wrapper,它只是缓存数据,所以它会在后续处理程序返回后可用:

  type MyResponseWriter struct {
http.ResponseWriter
buf * bytes.Buffer
}

func(mrw * MyResponseWriter)Write(p [] byte)(int,error){
return mrw.buf.Write(p)
}

请注意, MyResponseWriter.Write()只是将数据写入缓冲区。您也可以选择立即检查它(在 Write()方法中)并立即将数据写入包装/嵌入的 ResponseWriter 。你甚至可以修改数据。您必须完全控制。

虽然后续处理程序可能还会发送与响应数据相关的HTTP响应标头,例如长度或校验和 - 如果你改变了响应数据,这也可能会失效。



完整示例



将这些部分放在一起,这里有一个完整的工作示例:

  func loginmw(处理程序http.Handler)http.Handler {
返回http.HandlerFunc(func(w http.ResponseWriter,r * http.Request){
body,err:= ioutil.ReadAll(r.Body)
if err!= nil {
log.Printf(Error reading body:%v,err)
http.Error(w,can not read body,http.StatusBadRequest)
return
}

//工作/检查主体,甚至可以修改它!

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

//创建响应包装:
mrw:=& MyResponseWriter {
ResponseWriter:w,
buf:& bytes.Buffer {},
}

//调用下一个处理函数,传递响应包装:
handler.ServeHTTP(mrw,r)

//现在检查响应,最后发送出去:
//(你也可以在发送之前修改它!)
if _,err:= io.Copy(w,mrw.buf); err!= nil {
log.Printf(未能发出响应:%v,err)
}
})
}


I am writing my own logginMiddleware. Basically, I need to log body of the request and the response. The problem that I faced is that when I read body, it becomes empty and I cannot read it twice. I understand that it happens because it is of type ReadCloser. Is there a way to rewind body to the beginning?

解决方案

Inspecting and mocking request body

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.

One option is to read the whole body using ioutil.ReadAll(), which gives you the body as a byte slice.

You may use bytes.NewBuffer() to obtain an io.Reader from a byte slice.

The last missing piece is to make the io.Reader an io.ReadCloser, because bytes.Buffer does not have a Close() method. For this you may use ioutil.NopCloser() which wraps an io.Reader, and returns an io.ReadCloser, whose added Close() method will be a no-op (does nothing).

Note that you may even modify the contents of the byte slice you use to create the "new" body. You have full control over it.

Care must be taken though, as there might be other HTTP fields like content-length and checksums which may become invalid if you modify only the data. If subsequent handlers check those, you would also need to modify those too!

Inspecting / modifying response body

If you also want to read the response body, then you have to wrap the http.ResponseWriter you get, and pass the wrapper on the chain. This wrapper may cache the data sent out, which you can inspect either after, on on-the-fly (as the subsequent handlers write to it).

Here's a simple ResponseWriter wrapper, which just caches the data, so it'll be available after the subsequent handler returns:

type MyResponseWriter struct {
    http.ResponseWriter
    buf *bytes.Buffer
}

func (mrw *MyResponseWriter) Write(p []byte) (int, error) {
    return mrw.buf.Write(p)
}

Note that MyResponseWriter.Write() just writes the data to a buffer. You may also choose to inspect it on-the-fly (in the Write() method) and write the data immediately to the wrapped / embedded ResponseWriter. You may even modify the data. You have full control.

Care must be taken again though, as the subsequent handlers may also send HTTP response headers related to the response data –such as length or checksums– which may also become invalid if you alter the response data.

Full example

Putting the pieces together, here's a full working example:

func loginmw(handler http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        body, err := ioutil.ReadAll(r.Body)
        if err != nil {
            log.Printf("Error reading body: %v", err)
            http.Error(w, "can't read body", http.StatusBadRequest)
            return
        }

        // Work / inspect body. You may even modify it!

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

        // Create a response wrapper:
        mrw := &MyResponseWriter{
            ResponseWriter: w,
            buf:            &bytes.Buffer{},
        }

        // Call next handler, passing the response wrapper:
        handler.ServeHTTP(mrw, r)

        // Now inspect response, and finally send it out:
        // (You can also modify it before sending it out!)
        if _, err := io.Copy(w, mrw.buf); err != nil {
            log.Printf("Failed to send out response: %v", err)
        }
    })
}

这篇关于Golang阅读请求正文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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