go - 如何记录请求正文(通过中间件)而不进行缓冲?

标签 go

假设我有一个简单的服务器(为简洁起见,省略了错误处理):

func main() {
    http.HandleFunc("/", HelloServer)
    http.ListenAndServe(":8080", nil)
}

func HelloServer(w http.ResponseWriter, r *http.Request) {
    b, _ := ioutil.ReadAll(r.Body)
    fmt.Fprintf(w, "The request is: %s", string(b))
}

我需要添加一个简单的中间件,该中间件读取请求的主体并将其记录下来。我已经有了这个可行的解决方案:
func requestLogger(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        pr, pw := io.Pipe()
        tee := io.TeeReader(r.Body, pw)
        r.Body = pr
        go func() {
            body, _ := ioutil.ReadAll(tee)
            defer pw.Close()
            log.Printf("This is the logged request: %s", string(body))
        }()
        next(w, r)
    }
}

我通过反复试验直观地找到了该解决方案,所以我不确定在所有情况下该方法是否都有效。这些是关于我自己的解决方案的问题:
  • 为什么我必须手动关闭Pipe在r.Body上遇到EOF时,ioutil.ReadAll()不会将其关闭。
  • 为什么没有阻止? 有一个循环引用,因为TeeReader从它通过管道(r.Body = pr)写入的同一源(r.Body)读取。根据文档,“写入PipeWriter”块直到满足一个或多个读取为止;对于TeeReader,必须在读取完成之前完成写入。
  • 最佳答案

    ioutil.ReadAll()在遇到EOF时不会关闭它吗?



    Package ioutil

    func ReadAll

    func ReadAll(r io.Reader) ([]byte, error)
    

    ReadAll从r读取直到出现错误或EOF并返回数据
    读。


    那不是记录的行为。另外,io.Reader也不是io.ReadCloser

    关于go - 如何记录请求正文(通过中间件)而不进行缓冲?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59705848/

    相关文章:

    Go:未定义的类

    shell - python 的 shlex.split 替代 Go

    go - Go 中的相同算法、多种输入和输出类型的可能性?

    go - 如何将 go channel 值存储到其他数据类型(字符串,byte [])并将其重新分配给其他 go channel

    go - 在 Go 中为示例 gonum 应用程序声明类型

    http - 在一个 IP 上托管多个 Golang 站点并根据域请求提供服务?

    go - 分配错误 : runtime: out of memory

    去防止重复

    database - 什么时候需要提供CA文件?

    go - 具有许多嵌套的结构填充错误