|
我正在编写自己的登录中间件。基本上,我需要记录请求和响应的正文。我面临的问题是,当我阅读正文时,它变空了,我无法阅读两次。我知道它发生是因为它是 ReadCloser 类型。有没有办法把身体回到开始?
2 \. S4 ^' y6 w( J5 v. E
+ h! P( g0 O' n- f- U 解决方案: 3 K& f% \1 n G y, C# C3 d
检查和模拟请求文本当你第一次阅读文本时,你必须储存它,所以一旦你完成它,你可以设置一个新的io.ReadCloser作为从原始数据构造的请求正文。因此,当您在链中前进时,下一个处理程序可以读取相同的主体。( w9 d) D2 @3 B* w/ D* B
一种选择是 阅读整个主体ioutil.ReadAll(),它将主体作为字节切片。
0 i9 M- l# U3 {1 T" I8 n# j& d可以从字节切片中使用bytes.NewBuffer()获取io.Reader。
4 U* j5 p$ E- E* V, ] w- _( K: t最后缺少的部分是生产io.Readeran io.ReadCloser,因为bytes.Buffer没有Close()方法ioutil.NopCloser()which 包装 an io.Reader,并返回 an io.ReadCloser,其添加的Close()方法将是无操作(什么都不做)。9 n" e* F1 H% E$ y
请注意,您甚至可以修改用于创建新主体的字节切片。您可以完全控制它。# @, `: D% `9 z
但是要小心,因为可能还有其他 HTTP 字段,如内容长度和验证,如果只修改数据,可能无效。如果检查后续处理程序,您还需要修改它们!% n4 ?8 S8 p+ Z- @2 O
检查/修改响应文本如果您还想阅读响应文本,则必须包装http.ResponseWriter您得到的,并将包装器传输到链上。该包装器可以缓存发送的数据,并在运行后检查数据(当后续处理程序写入时)。
! Z, ?- M$ E1 L% g% ~1 x这很简单ResponseWriter包装器只缓存数据,只能在后续处理程序返回后使用:3 w& G* O5 k1 h7 h
type MyResponseWriter struct { http.ResponseWriter buf *bytes.Buffer}func (mrw *MyResponseWriter) Write(p []byte) (int,error) { return mrw.buf.Write(p)}, E: b4 r4 r- f6 ^
请注意,MyResponseWriter.Write()只将数据写入缓冲区。您也可以选择立即检查它(在Write()方法中)并立即将数据写入包装/嵌入ResponseWriter. 你甚至可以修改数据。你有完全的控制权。. H2 O! d* Q* k! o- k6 @& I. A, ]3 L
但必须再次小心,因为后续处理程序也可能发送与响应数据相关的 HTTP 响应标头-如长度或验证-如果您更改响应数据,这些标头也可能无效。% Q" G& ^. t; J3 d5 N! F
完整示例把这些部分放在一起是一个完整的工作示例:
; U% r/ H: z. w( c[code]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) }code] |
|