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

 更新时间:2024年08月30日 11:28:08   作者:二郎腿  
当使用Go语言进行 HTTP 请求时,默认情况下,http.Client 会自动处理服务器返回的重定向响应(3xx 状态码),本文将详细介绍如何在 Go 中实现禁止 HTTP 请求的重定向、限制重定向次数以及添加自定义重定向策略,需要的朋友可以参考下

一、背景

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

二、默认值

http.ClientCheckRedirect 字段是用于处理重定向策略的函数,如果 CheckRedirect 不是 nil,则客户端会在遵循 HTTP 重定向之前调用它。参数 req 和 via 是即将到来的请求和已经发出的请求,最早发出的请求在前面。如果 CheckRedirect 返回错误,则 ClientGet 方法将返回前一个 Response(其 Body 关闭)和 CheckRedirect 的错误(包装在 url.Error),而不是继续发出重定向请求。作为一种特殊情况,如果 CheckRedirect 返回 ErrUseLastResponse,则返回的最新响应体的 body, 且 body 未关闭,并返回 nil 错误。如果 CheckRedirectnil,则客户端使用其默认重定向策略,即在连续 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.ClientCheckRedirect 字段为一个为一个自定义的函数,可以控制重定向的行为。这个函数接收一个 *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请求重定向内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • golang结构体指针的实现

    golang结构体指针的实现

    这篇文章主要介绍了golang结构体指针的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-06-06
  • GoFrame框架Scan类型转换实例

    GoFrame框架Scan类型转换实例

    这篇文章主要为大家介绍了GoFrame框架Scan类型转换的实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • go slice 数组和切片使用区别示例解析

    go slice 数组和切片使用区别示例解析

    这篇文章主要为大家介绍了go slice 数组和切片使用区别示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • Golang字符串常用函数的使用

    Golang字符串常用函数的使用

    Golang提供了许多内置的字符串函数,这些函数可在处理字符串数据时帮助执行一些操作,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • 深入解析Go语言的io.ioutil标准库使用

    深入解析Go语言的io.ioutil标准库使用

    这篇文章主要介绍了Go语言的io.ioutil标准库使用,是Golang入门学习中的基础知识,需要的朋友可以参考下
    2015-10-10
  • golang中struct和interface的基础使用教程

    golang中struct和interface的基础使用教程

    Go不同于一般的面向对象语言,需要我们好好的学习研究,下面这篇文章主要给大家介绍了关于golang中struct和interface的基础使用的相关资料,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2018-03-03
  • 一文带你了解Golang中select的实现原理

    一文带你了解Golang中select的实现原理

    select是go提供的一种跟并发相关的语法,非常有用。本文将介绍 Go 语言中的 select 的实现原理,包括 select 的结构和常见问题、编译期间的多种优化以及运行时的执行过程
    2023-02-02
  • go语言中Timer和Ticker两种计时器的使用

    go语言中Timer和Ticker两种计时器的使用

    go语言中有Timer和Ticker这样的两种计时器,两种计时器分别实现了不同的计时功能,本文主要介绍了go语言中Timer和Ticker两种计时器的使用,感兴趣的可以了解一下
    2024-08-08
  • Go语言并发控制之sync.WaitGroup使用详解

    Go语言并发控制之sync.WaitGroup使用详解

    这篇文章主要为大家详细介绍了Go语言并发控制中sync.Map的原理与使用,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-02-02
  • go语言中的面向对象

    go语言中的面向对象

    Go不支持类,而是提供了结构体。结构体中可以添加属性和方法。这样可以将数据和操作数据的方法绑定在一起,实现与类相似的效果。这篇文章介绍了go语言中的面向对象,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07

最新评论