Http 如何倒带io.ReadCloser
我总是被一个io.ReadCloser的内容所困扰,然后忘记我以前读过它,当我再次读它时,我得到的是一个空的有效负载。我希望有人能为我的愚蠢做点什么。尽管如此,我认为我可以使用TeeReader,但它无法满足我在这里的期望: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
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{}
_,呃:=