GoLang - 遵循带有正文数据的 POST 请求的重定向

Rox*_*Ben 2 networking httpclient httprequest go

我想对 POST 请求使用相同的正文进行重定向。

来自 GO 来源 (client.go)

func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect, includeBody bool) {
    switch resp.StatusCode {
    case 301, 302, 303:
        redirectMethod = reqMethod
        shouldRedirect = true
        includeBody = false

        // RFC 2616 allowed automatic redirection only with GET and
        // HEAD requests. RFC 7231 lifts this restriction, but we still
        // restrict other methods to GET to maintain compatibility.
        // See Issue 18570.
Run Code Online (Sandbox Code Playgroud)

但有时服务器会为 POST 请求返回 302 Redirect,这意味着我需要将相同的正文发送到另一个位置。

在这种情况下我该怎么办?

func FollowRedirectForPost() {
    client := &http.Client{}
    
    req, _ := http.NewRequest(http.MethodPost, "example.com/test", strings.NewReader(url.Values{
        "key": {"value"},
        "key1":{"value1"},
    }.Encode()))

    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    client.Do(req) // If server returns 302 Redirect so it means I need make the same request with the same body to
    // a different location. Just imagine i replaced "example.com/test" to "example.com/redirect_url"
}
Run Code Online (Sandbox Code Playgroud)

Dyl*_*ink 5

来自RFC7231

服务器应该在响应中生成一个 Location 头字段,其中包含不同 URI 的 URI 引用。用户代理可以使用位置字段值进行自动重定向。服务器的响应负载通常包含一个简短的超文本注释,其中包含指向不同 URI 的超链接。

注意:由于历史原因,用户代理可能会将后续请求的请求方法从 POST 更改为 GET。如果不希望出现此行为,则可以使用 307(临时重定向)状态代码。

因此您可以遵循重定向,新的 URI 位于Location标头中。你不必这样做。您可以将方法更改为 GET,但不是必须这样做。因此本质上,您所做的任何事情都符合 RFC。

您可以通过提供函数来提供自己的重定向策略CheckRedirect基本上与客户端执行的操作redirectPostOn302相同if was和was :includeBodytrueredirectMethodPOST

func FollowRedirectForPost() {
    client := &http.Client{
        CheckRedirect: redirectPostOn302,
    }
    
    req, _ := http.NewRequest(http.MethodPost, "example.com/test", strings.NewReader(url.Values{
        "key": {"value"},
        "key1":{"value1"},
    }.Encode()))

    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    client.Do(req) // If server returns 302 Redirect so it means I need make the same request with the same body to
    // a different location. Just imagine i replaced "example.com/test" to "example.com/redirect_url"
}

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

    lastReq := via[len(via)-1]
    if req.Response.StatusCode == 302 && lastReq.Method == http.MethodPost {
        req.Method = http.MethodPost

        // Get the body of the original request, set here, since req.Body will be nil if a 302 was returned
        if via[0].GetBody != nil {
            var err error
            req.Body, err = via[0].GetBody()
            if err != nil {
                return err
            }
            req.ContentLength = via[0].ContentLength
        }
    }

    return nil
}
Run Code Online (Sandbox Code Playgroud)