一、背景
当使用 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 次。我们可以通过重写默认函数来禁止重定向、改变重定向次数以及添加自定义的重定向过滤逻辑。
到此这篇关于go重写http请求重定向的方法的文章就介绍到这了,更多相关go http请求重定向内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论