# 中间件

# 内建中间件

# 限制器 Limiter

限制器是一组Goreq提供的中间件。可以轻松的过滤可请求、设置延时、限制速率、并发。

如下,这个Client将不会接受发送向*.taobao.com的请求。

c := goreq.NewClient(goreq.WithFilterLimiter(true, &goreq.FilterLimiterOpinion{
  LimiterMatcher: goreq.LimiterMatcher{
    Glob: "*.taobao.com",
  },
  Allow: false,
}))
fmt.Println(c.Do(goreq.Get("https://www.taobao.com/")).Err) // ReqRejectedErr

# LimiterMatcher

在上面的例子中,出现了LimiterMatcher。Goreq的每个限制器都会要求设置这个参数。LimiterMatcher是用来表示,当前这个限制器将会对哪些Host起作用。

LimiterMatcher: goreq.LimiterMatcher{
	Glob: "*.taobao.com",
	Regexp: "(.*?).taobao.com,
},

GlobRegexp只能选择其一。若同时设置,将会使用Glob

# WithFilterLimiter

此限制器用于过滤请求。

func WithFilterLimiter(noneMatchAllow bool, opts ...*FilterLimiterOpinion) Middleware

noneMatchAllow表示经过此过滤器,未名中Matcher的请求是否允许放行。

c := goreq.NewClient(goreq.WithFilterLimiter(true, &goreq.FilterLimiterOpinion{
   LimiterMatcher: goreq.LimiterMatcher{
      Glob: "*.taobao.com",
   },
   Allow: false,
}))

# DelayLimiterOpinion

此限制器用于控制请求间的延时。

func WithDelayLimiter(eachSite bool, opts ...*DelayLimiterOpinion) Middleware

eachSite表示是否为不同的Matcher分别计时。

c = goreq.NewClient(goreq.WithDelayLimiter(false, &goreq.DelayLimiterOpinion{
   LimiterMatcher: goreq.LimiterMatcher{
      Glob: "*",
   },
   Delay: 5 * time.Second,
   // RandomDelay: 5 * time.Second,
}))

使用RandomDelay将从0到RandomDelay之间随机取一个时间延时。

# RateLimiterOpinion

此限制器用于控制发送请求的速率。

func WithRateLimiter(eachSite bool, opts ...*RateLimiterOpinion) Middleware

eachSite表示是否为不同的Matcher分别控制。

c = goreq.NewClient(goreq.WithRateLimiter(false, &goreq.RateLimiterOpinion{
   LimiterMatcher: goreq.LimiterMatcher{
      Glob: "*",
   },
   Rate: 2,
}))

# ParallelismLimiterOpinion

此限制器用于控制并发数量。

func WithParallelismLimiter(eachSite bool, opts ...*ParallelismLimiterOpinion) Middleware

eachSite表示是否为不同的Matcher分别控制。

c = goreq.NewClient(goreq.WithParallelismLimiter(false, &goreq.ParallelismLimiterOpinion{
   LimiterMatcher: goreq.LimiterMatcher{
      Glob: "*",
   },
   Parallelism: 2,
}))

# WithCookie

func WithCookie(urlAddr string, cookies ...*http.Cookie) Middleware

cookie jar添加cookie

c := goreq.NewClient(goreq.WithCookie("https://example.com", &http.Cookie{
   Name:  "token",
   Value: "admin",
}))

# WithCache

缓存响应。

func WithCache(ca *cache.Cache) Middleware
c := goreq.NewClient(goreq.WithCache(cache.New(1*time.Hour, 10*time.Minute)))

WithCache的缓存是基于对Request的Hash。具体可以查看utils.goGetRequestHash

# WithRetry

自动重试。

func WithRetry(maxTimes int, isRespOk func(*Response) bool) Middleware
  • maxTimes 最大重试次数。
  • isRespOk 用于判断请求是否成功的函数。若为nil则只检查Response.Err是否为nil

# WithProxy

自动配置代理。

func WithProxy(p ...string) Middleware

可以传入一或多个代理URL(如:http://localhost:1080)。若传入多个时将在每次请求随机选取一个。

若不传入参数,会自动使用all_proxyhttp_proxyhttps_proxy的环境变量。

# WithRefererFiller

自动把Referer头部填写为当前请求地址的根地址。用于处理防盗链。

func WithRefererFiller() Middleware

# WithRandomUA

当请求头部UA为空时,随机添加一个UA。

func WithRandomUA() Middleware

备选UA参考mw.go末尾。

# 开发中间件

# 中间件是什么

type Middleware func(*Client, Handler) Handler
type Handler func(*Request) *Response

中间件,即Middleware是给Client添加的一个处理函数(Handler)。Handler则是负责将Request转化为Response的函数。

例如使用WithRefererFiller中间件为例。

func WithRefererFiller() Middleware {
   return func(x *Client, h Handler) Handler {
      return func(req *Request) *Response {
         if req.Header.Get("Referer") == "" {
            req.AddHeader("Referer", req.URL.Scheme+"://"+req.URL.Host)
         }
         res := h(req)
         return res
      }
   }
}

最外层函数一般留作传入配置参数,比如WithProxy中间件就需要传入代理的配置信息。此函数返回的就是Middleware,即为中间件。

中间件Middleware将被交给Client调用,Client将传入的中间件组合嵌套,并在执行Do()时调用组合好的中间件队列。

# 执行顺序

func main() {
   c := goreq.NewClient(
      func(x *goreq.Client, h goreq.Handler) goreq.Handler {
         return func(req *goreq.Request) *goreq.Response {
            fmt.Println("middleware 1")
            return h(req)
         }
      },
      func(x *goreq.Client, h goreq.Handler) goreq.Handler {
         return func(req *goreq.Request) *goreq.Response {
            fmt.Println("middleware 2")
            return h(req)
         }
      },
   )
   goreq.Get("https://httpbin.org/get").SetClient(c).Do()
}

输出:

middleware 2
middleware 1

后添加的中间件会被先执行。

Last Updated: 3/13/2021, 7:37:21 AM