Unit test for gin's Context.Redirect works for GET response codes but not for POST response codes (golang)

WBOY
Release: 2024-02-10 19:27:08
forward
1155 people have browsed it

gin 的 Context.Redirect 的单元测试适用于 GET 响应代码,但不适用于 POST 响应代码 (golang)

When writing web applications in Go language, we often use the gin framework to handle HTTP requests and responses. When performing unit testing, we need to perform coverage testing on the code to ensure the quality and stability of the code. However, the unit test for gin's Context.Redirect method works great when handling GET requests, but not so well when handling POST requests. In this article, PHP editor Apple will explain in detail why this problem occurs and provide some solutions to unit test POST requests.

Question content

I want my server to redirect a specific endpoint to another server. The endpoint can be getted or posted. In both cases, the http response code should be 302. If I use curl on this code, it does show response code 302 in both cases, and curl -l follows the redirect correctly. Wow.

but

My unit test uses httptest.newrecorder() to capture the information, but it only works with get, not post. So I need to figure out how to get the unit tests to work when I know the actual redirection is working. The failing test shows that the http response code is 200 instead of 302 (http.statusfound).

$ go run foo.go
post code 200
get code 302
Copy after login

This is an independent test.

package main

import (
    "net/http"
    "net/http/httptest"
    "github.com/gin-gonic/gin"
)

func main() {
    gin.setmode(gin.releasemode)
    {
        w := httptest.newrecorder()
        context, _ := gin.createtestcontext(w)
        context.request = httptest.newrequest("post", "http://localhost:23632/foobar", nil)
        context.redirect(http.statusfound, "http://foobar.com")

        print("post code ",w.code,"\n")
    }

    {
        w := httptest.newrecorder()
        context, _ := gin.createtestcontext(w)
        context.request = httptest.newrequest("get", "http://localhost:23632/foobar", nil)
        context.redirect(http.statusfound, "http://foobar.com")

        print("get code ",w.code,"\n")
    }
}
Copy after login

When I execute curl post on the actual application (not shown) I see that it is working:

curl -v -XPOST localhost:23632/foobar
* About to connect() to localhost port 23632 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 23632 (#0)
> POST /foobar HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:23632
> Accept: */*
>
< HTTP/1.1 302 Found
< Location: http://foobar.com
< Vary: Origin
< Date: Tue, 23 May 2023 22:38:42 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact
Copy after login

Solution

tl;dr

The solution is to explicitly call context.writer.writeheadernow after context.redirect.

illustrate

This is an edge case of using the gin context returned from gin.createtestcontext.

For get requests, gin will eventually call http.redirect, which will write a short html body to the response (similar to <a href="http://foobar. com">found</a> ), causing the response's status code to be written.

For post requests, http.redirect no short html body is written, and the status code does not have a chance to be written to the response.

See http implementation. Redirection. According to the source code, if the content-type header was set before, the same problem will occur with the get request:

{
    w := httptest.newrecorder()
    context, _ := gin.createtestcontext(w)
    context.request = httptest.newrequest("get", "http://localhost:23632/foobar", nil)
+   context.header("content-type", "text/html")
    context.redirect(http.statusfound, "http://foobar.com")
    print("get code ", w.code, "\n")
  }
Copy after login

The solution is to explicitly call context.writer.writeheadernow:

{
    w := httptest.NewRecorder()
    context, _ := gin.CreateTestContext(w)
    context.Request = httptest.NewRequest("POST", "http://localhost:23632/foobar", nil)
    context.Redirect(http.StatusFound, "http://foobar.com")
+   context.Writer.WriteHeaderNow()

    print("POST code ", w.Code, "\n")
  }
Copy after login

gin itself uses the same workaround. See testcontextrenderredirectwithrelativepath.

A real server application won't suffer from the same problem, because (*engine).handlehttprequest will call writeheadernow for us (see source code). That's why I call it an "edge case" rather than a "bug".

The above is the detailed content of Unit test for gin's Context.Redirect works for GET response codes but not for POST response codes (golang). For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:stackoverflow.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template