duanjuan3931 2017-03-25 19:36
浏览 50
已采纳

Golang阅读请求正文

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?

  • 写回答

2条回答 默认 最新

  • doucaishi0077 2017-03-25 19:54
    关注

    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)
            }
        })
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 完全没有学习过GAN,看了CSDN的一篇文章,里面有代码但是完全不知道如何操作
  • ¥15 使用ue5插件narrative时如何切换关卡也保存叙事任务记录
  • ¥20 软件测试决策法疑问求解答
  • ¥15 win11 23H2删除推荐的项目,支持注册表等
  • ¥15 matlab 用yalmip搭建模型,cplex求解,线性化处理的方法
  • ¥15 qt6.6.3 基于百度云的语音识别 不会改
  • ¥15 关于#目标检测#的问题:大概就是类似后台自动检测某下架商品的库存,在他监测到该商品上架并且可以购买的瞬间点击立即购买下单
  • ¥15 神经网络怎么把隐含层变量融合到损失函数中?
  • ¥15 lingo18勾选global solver求解使用的算法
  • ¥15 全部备份安卓app数据包括密码,可以复制到另一手机上运行