Go重写http请求重定向的方法

Go重写http请求重定向的方法

一、背景

当使用 Go 语言进行 HTTP 请求时,默认情况下,http.Client 会自动处理服务器返回的重定向响应(3xx 状态码)。但有时候,我们可能需要在请求中禁止自动的重定向。本文将详细介绍如何在 Go 中实现禁止 HTTP 请求的重定向、限制重定向次数以及添加自定义重定向策略。

二、默认值

http.Client 的 CheckRedirect 字段是用于处理重定向策略的函数,如果 CheckRedirect 不是 nil,则客户端会在遵循 HTTP 重定向之前调用它。参数 req 和 via 是即将到来的请求和已经发出的请求,最早发出的请求在前面。如果 CheckRedirect 返回错误,则 Client 的 Get 方法将返回前一个 Response(其 Body 关闭)和 CheckRedirect 的错误(包装在 url.Error),而不是继续发出重定向请求。作为一种特殊情况,如果 CheckRedirect 返回 ErrUseLastResponse,则返回的最新响应体的 body, 且 body 未关闭,并返回 nil 错误。如果 CheckRedirect 为 nil,则客户端使用其默认重定向策略,即在连续 10 个请求后停止。相关源码如下,来自src/net/http/client.go。

func defaultCheckRedirect(req *Request, via []*Request) error {if len(via) = 10 {return errors.New("stopped after 10 redirects")}return nil}

三、禁止重定向

通过设置 http.Client 的 CheckRedirect 字段为一个为一个自定义的函数,可以控制重定向的行为。这个函数接收一个 *http.Request 和一个 []*http.Request 参数,前者代表当前正在处理的请求,后者代表已经请求的重定向请求链,返回 http.ErrUseLastResponse 错误,收到这个错误后,http.Client 不会再继续重定向请求,并且返回一个 nil 错误给上游,如下:

func forbidRedirect(req *http.Request, via []*http.Request) (err error) {// 返回一个错误,表示不允许重定向return http.ErrUseLastResponse}

如果 CheckRedirect 字段不设置值,或是设置 nil 值,都会采用上述的默认函数defaultCheckRedirect,进行最多 10 重定向;如果返回的不是 http.ErrUseLastResponse 错误,那幺该请求将会收到一个非 nil 的错误。

四、更改重定向次数

即更改对 []*http.Request 参数长度的限制即可,一定不能返回 http.ErrUseLastResponse 错误。

func limitRedirect(req *http.Request, via []*http.Request) error {// via 记录了已经请求的 url 个数if len(via) = 3 {return errors.New("stopped after max redirects")}return nil}

五、自定义重定向策略

通过对重定向函数的重写,添加一些自定义的逻辑,并将该函数其赋值给 http client 的CheckRedirect,可以实现自定义重定向策略,其中 req.Response 参数表示导致该次重定向的返回。

func myRedirect(req *http.Request, via []*http.Request) error {// 限制重定向次数if len(via) = 10 {return errors.New("stopped after 10 redirects")}if req == nil || req.URL == nil || req.Response == nil || !strings.HasPrefix(req.Response.Status, "3") {return http.ErrUseLastResponse}// 禁止重定向下载 apk 文件if strings.HasSuffix(req.URL.Path, "apk") {return fmt.Errorf("invalid redirect url, path: %s", req.URL.Path)}// 限制重定向请求类型contentType := req.Response.Header.Get("Content-Type")if strings.Contains(contentType, "octet-stream") {return fmt.Errorf("invalid redirect url, type: %s", contentType)}// 限制重定向请求体长度contentLength := req.Response.Header.Get("Content-Length")if contentLength != "" {length, _ := strconv.Atoi(contentLength)if length 1000 {return fmt.Errorf("invalid redirect url, len: %s", contentLength)}}// 限制重定向请求传输编码transferEncoding := req.Response.Header.Get("Transfer-Encoding")if strings.Contains(transferEncoding, "chunked") {return fmt.Errorf("invalid redirect url, encoding: %s", transferEncoding)}return http.ErrUseLastResponse}

六、完整示例

package mainimport ("errors""fmt""io""net/http")func forbidRedirect(req *http.Request, via []*http.Request) (err error) {// 返回一个错误,表示不允许重定向return http.ErrUseLastResponse}func limitRedirect(req *http.Request, via []*http.Request) error {// via 记录了已经请求的 url 个数if len(via) = 3 {return errors.New("stopped after max redirects")}return nil}func main() { // 创建一个自定义的 HTTP Client client := http.Client{ CheckRedirect: forbidRedirect, } // 创建一个 GET 请求,该 url 一定要重定向 req, err := http.NewRequest("GET", "https://weibo.com", nil) if err != nil { fmt.Println("Error creating request:", err) return } // 使用自定义的 Client 发送请求 resp, err := client.Do(req) if err != nil { fmt.Println("Error sending request:", err) return } defer resp.Body.Close() fmt.Println("Response Status:", resp.Status)body, _ := io.ReadAll(resp.Body)fmt.Println("Response Body:", string(body))}

七、总结

http.Client 的 CheckRedirect 字段是用于处理重定向策略的函数,如果不赋值时就会采用默认函数,默认最多重定向 10 次。我们可以通过重写默认函数来禁止重定向、改变重定向次数以及添加自定义的重定向过滤逻辑。

Go重写http请求重定向的方法