测试c.Redirect()需用httptest.NewRequest+httptest.NewRecorder调用router.ServeHTTP(),设gin.TestMode,验证301/302状态码及Location头;非法URL致panic,须用assert.Panics()覆盖;中间件重定向后必须return或Abort(),否则触发多重WriteHeader panic。

测试 c.Redirect() 必须用 httptest.NewRequest + httptest.NewRecorder
直接调用 handler 函数(比如 handler(c))不会触发重定向逻辑,因为 c.Redirect() 依赖 c.Writer 的底层实现来写状态码和 Location header。不走 HTTP 流程,就拿不到真实响应头。
正确做法是构造请求、喂给 router,再检查 recorder 的结果:
- 必须提前调用
gin.SetMode(gin.TestMode),否则日志中间件会干扰断言,甚至 panic -
router.ServeHTTP(recorder, req)是唯一能完整模拟 HTTP 生命周期的方式 - 重定向的判断依据是:状态码为
301或302,且recorder.Header().Get("Location")非空
func TestRedirectHandler(t *testing.T) {
gin.SetMode(gin.TestMode)
r := gin.New()
r.GET("/old", func(c *gin.Context) {
c.Redirect(http.StatusFound, "https://example.com/new")
})
req, _ := http.NewRequest("GET", "/old", nil)
rec := httptest.NewRecorder()
r.ServeHTTP(rec, req)
assert.Equal(t, http.StatusFound, rec.Code)
assert.Equal(t, "https://example.com/new", rec.Header().Get("Location"))
}
重定向目标是相对路径或协议缺失 → 测试会 panic,不是 404
c.Redirect() 内部调用 url.Parse(),如果传入 "//example.com"、"/login" 或 "www.example.com",会直接 panic:net/url: invalid URL。这不是业务错误,是测试必须捕获的崩溃点。
- 测试时要覆盖非法输入场景,用
assert.Panics()显式验证 panic 是否发生 - 生产代码里必须确保第二个参数是绝对 URL(含
http://或https://) - 别指望中间件兜底——panic 发生在
c.Redirect()调用瞬间,后续逻辑全跳过
测试内部转发(c.Request.URL.Path + r.HandleContext())不能靠 Location header
这种“伪重定向”不发新请求,也不改地址栏,更不会设置 Location。它只是把当前请求上下文悄悄交给另一个已注册路由处理。测试重点变成:最终响应是否匹配目标路由的预期输出。
- 目标路径(如
/test2)必须已注册,否则r.HandleContext(c)返回 404,且无提示 - 查询参数不会自动继承,需手动复制:
c.Request.URL.RawQuery = c.Request.URL.Query().Encode() - 断言时检查响应 body 和 status code,而不是 header;例如
assert.JSONEq(t, `{"hello":"world"}`, rec.Body.String())
中间件里调了 c.Redirect() 却没 return → 测试会暴露双重写 header 错误
常见疏忽:在中间件中调用 c.Redirect() 后忘了 return,导致后续 handler 继续执行并尝试写 response,触发 http: multiple response.WriteHeader calls panic。
单元测试能立刻暴露这个问题——recorder 在第二次 WriteHeader 时直接 panic。修复方式只有两个:
- 重定向后立即
return(推荐) - 或用
c.Abort()阻断后续 chain(但要注意c.Abort()不影响已写的 header/body)
这个坑在线上可能表现为偶发 500,但在测试里是稳定 panic,务必在每个含 Redirect() 的中间件测试用例里加 assert.NotPanics()。


















