[Go] 禁止 net/http 包中 http.Client 的自动重定向

陪她去流浪 桃子 2019年06月27日 编辑 阅读次数:10048

Go 语言标准库的 net/http 包中的 http.Client 默认会自动跟随(follow)重定向,大多数时候,这种自动化的跟随是有意义的。比如:自动把 HTTP 连接重定向到 HTTPS 连接。 但是,也有很多时候这种自动重定向会带来麻烦,或是不必要。比如:常用的模拟 POST 登录方式。

常见的 POST 登录方式

  • 浏览器 POST /login
  • 服务器返回 302 Found,并携带 Cookie 和新的登录后的页面 /index
  • 浏览器重新发起到 /index 的请求

如果直接使用 http.Post 并登录成功的话,最终的页面是/index,中间的重定向相关的数据是拿不到的,包括 Cookie!

而我最近刚做一个命令行的路由器客户端,它就需要将上面过程中第二步返回的 Cookie 拿到,并用作后续 API 请求的验证。 所以,得想办法禁止 http.Client 的自动重定向。

http.Client 中的重定向

http.Client 中有一个跟重定向相关的字段CheckRedirect

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 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

从注释来看,这是一个函数字段,用于指定处理重定向的策略:

  • 如果这个字段不为空,Client将在每次重定向之前先调用它。如果这个函数返回了一个特殊的ErrUseLastResponse错误(Go1.7引入),那么将使用最近一次的请求响应作为返回;

    1
    2
    3
    4
    5
    6
    
    // Sentinel error to let users select the
    // previous response, without closing its
    // body. See Issue 10069.
    if err == ErrUseLastResponse {
        return resp, nil
    }
    
  • 如果这个字段为空,Client 将采用默认的重定向策略:将在最多连续重定向 10 次后停止;

如何禁止重定向

不使用默认的 http.Gethttp.Post 等方法,而是自定义一个带不重定向功能的 Client:

1
2
3
4
5
client := &http.Client{
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
        return http.ErrUseLastResponse
    },
}

然后调用 client 的 client.Getclient.Postclient.Do等方法即可。

参考

标签:HTTP · Go · 重定向