go 重写 http 请求重定向一、背景 当使用 Go 语言进行 HTTP 请求时,默认情况下,http.Client
个人主页:二郎腿 (erlangtui.top)
一、背景
当使用 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 main
import (
"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 次。我们可以通过重写默认函数来禁止重定向、改变重定向次数以及添加自定义的重定向过滤逻辑。
转载自:https://juejin.cn/post/7408391774635065396