在使用Go語(yǔ)言編寫(xiě)Web應(yīng)用程序時(shí),我們經(jīng)常會(huì)用到gin框架來(lái)處理HTTP請(qǐng)求和響應(yīng)。在進(jìn)行單元測(cè)試時(shí),我們需要對(duì)代碼進(jìn)行覆蓋測(cè)試,以保證代碼的質(zhì)量和穩(wěn)定性。然而,針對(duì)gin的Context.Redirect方法的單元測(cè)試在處理GET請(qǐng)求時(shí)非常適用,但在處理POST請(qǐng)求時(shí)卻不太適用。在本文中,php小編蘋(píng)果將會(huì)詳細(xì)解釋為什么這個(gè)問(wèn)題會(huì)出現(xiàn),并提供一些解決方案來(lái)進(jìn)行POST請(qǐng)求的單元測(cè)試。
問(wèn)題內(nèi)容
我希望我的服務(wù)器將特定端點(diǎn)重定向到另一臺(tái)服務(wù)器。該端點(diǎn)可以是 get
ted 或 post
ed。在這兩種情況下,http 響應(yīng)代碼都應(yīng)為 302。如果我在此代碼上使用 curl
,它確實(shí)在兩種情況下都顯示響應(yīng)代碼 302,并且 curl -l
正確遵循重定向。哇哦。
但是
我的單元測(cè)試使用httptest.newrecorder()
來(lái)捕獲信息,但它僅適用于get
,不適用于post
。因此,當(dāng)我知道實(shí)際的重定向正在工作時(shí),我需要弄清楚如何讓單元測(cè)試工作。失敗測(cè)試顯示http響應(yīng)代碼是200而不是302(http.statusfound
)。
$ go run foo.go post code 200 get code 302
登錄后復(fù)制
這是獨(dú)立測(cè)試。
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") } }
登錄后復(fù)制
當(dāng)我在實(shí)際應(yīng)用程序(未顯示)上執(zhí)行 curl post 時(shí),我發(fā)現(xiàn)它正在工作:
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
登錄后復(fù)制
解決方法
tl;dr
解決方法是在 context.redirect
之后顯式調(diào)用 context.writer.writeheadernow
。
說(shuō)明
這是使用從 gin.createtestcontext
返回的 gin 上下文的一個(gè)極端情況。
對(duì)于 get 請(qǐng)求,gin 最終會(huì)調(diào)用 http.redirect
,它將向響應(yīng)寫(xiě)入一個(gè)簡(jiǎn)短的 html 正文(類似于 found
),從而導(dǎo)致要寫(xiě)入響應(yīng)的狀態(tài)代碼。
對(duì)于 post 請(qǐng)求,http.redirect
不會(huì)寫(xiě)入短 html 正文,并且狀態(tài)代碼沒(méi)有機(jī)會(huì)寫(xiě)入響應(yīng)。
參見(jiàn)http 的實(shí)現(xiàn).重定向。根據(jù)源碼,如果之前設(shè)置了content-type
header,那么get請(qǐng)求也會(huì)出現(xiàn)同樣的問(wèn)題:
{ 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") }
登錄后復(fù)制
解決方法是顯式調(diào)用 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") }
登錄后復(fù)制
gin 本身使用相同的解決方法。請(qǐng)參閱 testcontextrenderredirectwithrelativepath。
真正的服務(wù)器應(yīng)用程序不會(huì)遇到同樣的問(wèn)題,因?yàn)?(*engine).handlehttprequest
將為我們調(diào)用 writeheadernow
(請(qǐng)參閱 源代碼)。這就是為什么我稱其為“極端情況”而不是“錯(cuò)誤”。