原理
NopCloser
的原理很简单,就是将一个不带 Close
的 Reader
封装成 ReadCloser
。以下是源码:
// NopCloser returns a ReadCloser with a no-op Close method wrapping
// the provided Reader r.
func NopCloser(r Reader) ReadCloser {
return nopCloser{
r}
}
type nopCloser struct {
Reader
}
func (nopCloser) Close() error {
return nil }
使用场景
我有一个服务 A
和服务 B
,主要负责数据存储,用户请求会发送给服务 A
,此时服务 A
会去检查用户发送的数据内容,若是有重要的信息,则会将该用户发送的数据转发给服务 B
。此时为服务 A
添加一个中间件拦截请求,在此判断是否要将数据发送给服务 B
。
示意图:
示意代码:
这里只给服务 A
的相关代码。由于已经读取过 Request
的 Body
数据了,后续再读会读不到,所以这里需要自己重新再构建一个 ReadCloser
赋值给原先的 Body
,用 NopCloser
简单的包装一下。
package main
import (
"bytes"
"github.com/gin-gonic/gin"
"io/ioutil"
"net/http"
)
func main() {
engineA := gin.Default()
engineA.Use(hasXiaohong)
engineA.Run(":12345")
}
func hasXiaohong(c *gin.Context) {
data, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
c.String(http.StatusBadRequest, err.Error())
c.Abort()
return
}
defer c.Request.Body.Close()
if bytes.Contains(data, []byte("小红")) {
// 发送给服务 B
// 数据封装
http.Post("服务 B 的地址", "contentType", bytes.NewBuffer(data))
}
// 注意这时 c.Request.Body 已经读完了,需要重新将读出来的值给放回去,之后的处理就依然可以使用 c.Request.Body 了。
bufReader := bytes.NewBuffer(data) // 这只是一个 Reader
c.Request.Body = ioutil.NopCloser(bufReader) // 这边通过 NopCloser 包装成 ReadCloser
}