环境
go1.13.5
分析
平时我们通过标准库net/http发起http请求后如果遇到重定向后会继续发起跳转请求得到最后的响应返回给我们。
但是当我们需要获取重定向的相关信息的时候怎么办呢?
我们是通过Client.Do方法发起的请求,那我们可以从这里入手。打开源码可以看到如下注释:
If the server replies with a redirect, the Client first uses the CheckRedirect function to determine whether the redirect should be followed. If permitted, a 301, 302, or 303 redirect causes subsequent requests to use HTTP method GET (or HEAD if the original request was HEAD), with no body. A 307 or 308 redirect preserves the original HTTP method and body, provided that the Request.GetBody function is defined. The NewRequest function automatically sets GetBody for common standard library body types.
可以看到在遇到重定向的时候会先通过CheckRedirect方法来检测是否要跳转。
直接看源码for里面流程,获取到响应后,首先会通过c.checkRedirect检测是否可以跳转,如果返回了一个非nil的error则结束请求,否者继续执行后面的逻辑。
注意下面有个err是否为ErrUseLastResponse的判断,我们稍后再说。
err = c.checkRedirect(req, reqs)
// Sentinel error to let users select the
// previous response, without closing its
// body. See Issue 10069.
if err == ErrUseLastResponse {
return resp, nil
}
// ....省略
if err != nil {
// Special case for Go 1 compatibility: return both the response
// and an error if the CheckRedirect function failed.
// See https://golang.org/issue/3795
// The resp.Body has already been closed.
ue := uerr(err)
ue.(*url.Error).URL = loc
return resp, ue
}
来到redirectBehavior方法来觉得遇到3XX状态码的相关处理,它会返回的shouldRedirect将最后决定是否需要跳转。
var shouldRedirect bool
redirectMethod, shouldRedirect, includeBody = redirectBehavior(req.Method, resp, reqs[0])
if !shouldRedirect {
return resp, nil
}
通过追踪c.checkRedirect发现它会获取c.CheckRedirect,如果c.CheckRedirect是nil,则调用默认的defaultCheckRedirect来控制跳转次数。
// checkRedirect calls either the user's configured CheckRedirect
// function, or the default.
func (c *Client) checkRedirect(req *Request, via []*Request) error {
fn := c.CheckRedirect
if fn == nil {
fn = defaultCheckRedirect
}
return fn(req, via)
}
func defaultCheckRedirect(req *Request, via []*Request) error {
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
}
再来看看c.CheckRedirect:
// CheckRedirect specifies the policy for handling redirects.
// If CheckRedirect is not nil, the client calls it before
// following an HTTP redirect. The arguments req and via are
// the upcoming request and the requests made already, oldest
// first. If CheckRedirect returns an error, the Client's Get
// method returns both the previous Response (with its Body
// closed) and CheckRedirect's error (wrapped in a url.Error)
// instead of issuing the Request req.
// As a special case, if CheckRedirect returns ErrUseLastResponse,
// then the most recent response is returned with its body
// unclosed, along with a nil error.
//
// If CheckRedirect is nil, the Client uses its default policy,
// which is to stop after 10 consecutive requests.
CheckRedirect func(req *Request, via []*Request) error
通过注释可以看出CheckRedirect是用来控制跳转的,而当它返回一个ErrUseLastResponse的error就会返回最近一次响应体。这里和上面c.checkRedirect下面的判断相呼应,当接受到ErrUseLastResponse是就退出了循环体。这下就明白了,只要我们给CheckRedirect传入自定义的func并返回ErrUseLastResponse类型的error就可以我们自己控制跳转了。
结果
发起请求时给http.Client的CheckRedirect传入返回http.ErrUseLastResponse的func即可。
例:
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return "", err
}
c := http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Timeout: 30 * time.Second,
}
resp, err := c.Do(req)
本文深入解析Go语言中HTTP客户端如何处理重定向,介绍通过自定义CheckRedirect函数控制重定向行为的方法,实现对重定向响应的精确捕捉。
8437

被折叠的 条评论
为什么被折叠?



