Http 如何倒带io.ReadCloser

Http 如何倒带io.ReadCloser,http,go,Http,Go,我总是被一个io.ReadCloser的内容所困扰,然后忘记我以前读过它,当我再次读它时,我得到的是一个空的有效负载。我希望有人能为我的愚蠢做点什么。尽管如此,我认为我可以使用TeeReader,但它无法满足我在这里的期望: func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { buf := &bytes.Buffer{} tee := i

我总是被一个io.ReadCloser的内容所困扰,然后忘记我以前读过它,当我再次读它时,我得到的是一个空的有效负载。我希望有人能为我的愚蠢做点什么。尽管如此,我认为我可以使用TeeReader,但它无法满足我在这里的期望:

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        buf := &bytes.Buffer{}
        tee := io.TeeReader(r.Body, buf)
        body, err := ioutil.ReadAll(tee)
        if err != nil {
            http.Error(w, err.Error(), http.StatusBadRequest)
            return
        }
        log.Println("body", string(body))
        payload, err := httputil.DumpRequest(r, true)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        log.Println("dump request", string(payload))
        w.WriteHeader(http.StatusOK)
    })
    log.Fatal(http.ListenAndServe(":8080", nil))
}
我的“转储请求”日志行中缺少尸体

也就是说,当我运行
curl-I-X POST--data'{“username”:“xyz”,“password”:“xyz”}http://localhost:8080

我想要完整的原始请求:

2019/01/14 11:09:50 dump request POST / HTTP/1.1
Host: localhost:8080
Accept: */*
Content-Length: 35
Content-Type: application/x-www-form-urlencoded
User-Agent: curl/7.63.0

{"username":"xyz","password":"xyz"}
我错过了什么

如何在Go[…]中倒带io.ReadCloser

你不能。可以读取和关闭ReadCloser。除非实际的底层类型有某种方法可以倒带,否则您根本无法倒带。 (对于您的情况,可能在通过io/ioutil.ReadCloser添加Close方法作为Request.Body之后,您可以使用bytes.Buffer;但这不是“倒带”,而是“替换”。)

如何在Go[…]中倒带io.ReadCloser

你不能。可以读取和关闭ReadCloser。除非实际的底层类型有某种方法可以倒带,否则您根本无法倒带。
(对于您的情况,您可以只使用bytes.Buffer,可能是在通过io/ioutil.ReadCloser添加Close方法作为Request.Body之后;但这不是“倒带”,而是“替换”。

您不能倒带
io.ReadCloser
,除非基础值也是一个

根据定义,
io.ReadCloser
有两种方法:
Read
Close
。因此,显然没有倒带的选择

相比之下,
io.readseek
有两种方法:
Read
Seek
,后者允许倒带

如果您只需要接受同样可查找的io.ReadClosers,您可以轻松地将这两个选项组合起来:

type ReadSeekCloser interface {
    io.Reader
    io.Seeker
    io.Closer
}
现在,您可以使用自定义的
ReadSeekCloser
类型代替
io.ReadCloser
,您还可以选择倒带阅读器


当然,野外很少有符合此接口的
io.ReadCloser
s(
os.File
将是主要的接口)。如果您有一个未实现
Seek
方法的
io.ReadCloser
(例如网络流),使其能够
Seek
的最简单方法可能是将内容转储到文件中,然后打开该文件。还有其他方法可以使内存中的缓冲区可查找(
bytes.NewReader
),但不同的方法需要先将流读入内存或磁盘。

除非底层值也是一个值,否则不能将
io.ReadCloser
倒带

根据定义,
io.ReadCloser
有两种方法:
Read
Close
。因此,显然没有倒带的选择

相比之下,
io.readseek
有两种方法:
Read
Seek
,后者允许倒带

如果您只需要接受同样可查找的io.ReadClosers,您可以轻松地将这两个选项组合起来:

type ReadSeekCloser interface {
    io.Reader
    io.Seeker
    io.Closer
}
现在,您可以使用自定义的
ReadSeekCloser
类型代替
io.ReadCloser
,您还可以选择倒带阅读器


当然,野外很少有符合此接口的
io.ReadCloser
s(
os.File
将是主要的接口)。如果您有一个未实现
Seek
方法的
io.ReadCloser
(例如网络流),使其能够
Seek
的最简单方法可能是将内容转储到文件中,然后打开该文件。还有其他方法可以使内存中的缓冲区可查找(
bytes.NewReader
),但不同的方法首先需要将流读入内存或磁盘。

请求主体无法查找。没有倒带,它们不在内存中缓冲。读取该数据时,将从网络流中读取该数据。想象一下,一个大的上传,默认情况下将所有数据缓冲在内存中是浪费的

也就是说,你可以做一些事情来获得你想要的输出。它包括在你读过它之后替换r.Body

你提到想要一张皮棉支票。我发现声明缓冲区并仅使用该缓冲区会有所帮助。如果你愿意的话,你甚至可以用这个缓冲器来代替r.Body。您仍然需要记住使用Seek“倒带”


func main(){
http.HandleFunc(“/”,func(w http.ResponseWriter,r*http.Request){
b:=&bytes.Buffer{}
_,err:=io.Copy(b,r.Body)
如果错误!=零{
http.Error(w,err.Error(),http.StatusBadRequest)
回来
}
bodyCopy:=字节。新读取器(b.字节()
log.Println(“body”,b.String())
r、 Body=io.NoCloser(bodyCopy)
bodyCopy.Seek(0,0)
有效负载,err:=httputil.DumpRequest(r,true)
如果错误!=零{
Error(w,err.Error(),http.StatusInternalServerError)
回来
}
log.Println(“转储请求”,字符串(有效负载))
w、 WriteHeader(http.StatusOK)
})
log.Fatal(http.ListenAndServe(“:8080”,nil))
}

请求主体无法搜索。没有倒带,它们不在内存中缓冲。读取该数据时,将从网络流中读取该数据。想象一下,一个大的上传,默认情况下将所有数据缓冲在内存中是浪费的

也就是说,你可以做一些事情来获得你想要的输出。它包括在你读过它之后替换r.Body

你提到想要一张皮棉支票。我发现声明缓冲区并仅使用该缓冲区会有所帮助。如果你愿意的话,你甚至可以用这个缓冲器来代替r.Body。您仍然需要记住使用Seek“倒带”


func main(){
http.HandleFunc(“/”,func(w http.ResponseWriter,r*http.Request){
b:=&bytes.Buffer{}
_,呃:=