我总是因为阅读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))
}
Run Code Online (Sandbox Code Playgroud)
我的“转储请求”日志行中缺少正文。
即当我跑步时 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"}
Run Code Online (Sandbox Code Playgroud)
我错过了什么?
您不能倒回io.ReadCloser,除非基础值也是io.ReadSeeker。
An io.ReadCloser,根据定义,正好有两种方法:Read和Close。所以显然没有倒带的选项。
io.ReadSeeker相比之下,An有两种方法:Readand Seek,后者允许倒带。
如果您只需要接受io.ReadCloser也是可查找的 s,您可以轻松地将这两者结合起来:
type ReadSeekCloser interface {
io.Reader
io.Seeker
io.Closer
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以使用自定义ReadSeekCloser类型代替io.ReadCloser,并且可以选择倒回阅读器。
当然,io.ReadCloser实际上很少有s 真正符合这个接口(os.File将是主要的)。如果您有一个io.ReadCloser没有实现该Seek方法的(例如网络流),可能最简单的方法Seek是将内容转储到文件中,然后打开该文件。还有其他方法可以使内存中的缓冲区可bytes.NewReader查找(例如),但首先需要将流读入内存或磁盘。