Golang获取重定向信息

环境

  • 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检测是否可以跳转,如果返回了一个非nilerror则结束请求,否者继续执行后面的逻辑。

注意下面有个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.CheckRedirectnil,则调用默认的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是用来控制跳转的,而当它返回一个ErrUseLastResponseerror就会返回最近一次响应体。这里和上面c.checkRedirect下面的判断相呼应,当接受到ErrUseLastResponse是就退出了循环体。这下就明白了,只要我们给CheckRedirect传入自定义的func并返回ErrUseLastResponse类型的error就可以我们自己控制跳转了。

结果

发起请求时给http.ClientCheckRedirect传入返回http.ErrUseLastResponsefunc即可。

例:

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)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值