英文:
Redirects return http: multiple response.WriteHeader calls
问题
我正在使用Jon Calhoun的Go MVC框架(https://github.com/joncalhoun/viewcon)。
该框架仅依赖于julienschmidt/httprouter(https://github.com/julienschmidt/httprouter)。
我有一个类似于示例中的main方法:
func main() {
// 注册路由
router := httprouter.New()
// 默认路由
router.GET("/", controllers.Login.Perform(controllers.Login.Index))
// 登录路由
router.GET("/login", controllers.Login.Perform(controllers.Login.Login))
router.POST("/login", controllers.Login.Perform(controllers.Login.PostLogin))
// 仪表盘路由
router.GET("/dashboard", controllers.Dashboard.Perform(controllers.Dashboard.Index))
// 监听并处理请求
log.Fatal(http.ListenAndServe(":"+helpers.ReadConfig("port_http"), router))
}
我向登录URL发出POST请求,它调用以下方法:
func (self LoginController) PostLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params) error {
// 创建API URL
var url = helpers.ReadConfig("api") + "login"
// 填充要提交的模型
login := models.LoginModel{
Password: r.FormValue("password"),
Email: r.FormValue("username"),
}
// 从模型中生成JSON
bytes, err := json.Marshal(login)
if err != nil {
panic(err)
}
// 调用API的POST方法
var resp = helpers.ApiPost(url, r, string(bytes))
// 检查响应是否成功
if resp.Code != constants.ApiResp_Success {
// TODO: 处理API错误
login.Password = ""
errors := make(map[int]string)
errors[1] = "请提供有效的凭据。"
login.Common = models.CommonModel{
ErrorList: errors,
}
return views.Login.Index.Render(w, login, helpers.AcceptsGzip(r))
}
log.Println("---Redirect--")
http.Redirect(w, r, "/dashboard", 307)
log.Println("-----")
return views.Dashboard.Index.Render(w, login, helpers.AcceptsGzip(r))
}
基本上,如果登录不正确,我返回相同的视图。如果登录正确,我想重定向到另一个控制器中的方法。
然而,当我调用http.Redirect(w, r, "/dashboard", 307)
时,它返回以下错误:
http: multiple response.WriteHeader calls
我不确定为什么会发生这种情况,但我怀疑是因为我的监听器调用了Perform函数,该函数创建了一个http.handler,如下所示:
func (c *Controller) Perform(a Action) httprouter.Handle {
return httprouter.Handle(
func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// 设置响应头
// TODO: 设置适当的响应头
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Cache-Control", "public, max-age=0")
w.Header().Set("Token", "NOT-A-VALID-TOKEN")
w.WriteHeader(200)
if err := a(w, r, ps); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
}
有人知道如何在这个MVC框架中进行重定向吗?或者有一个临时解决方案吗?
英文:
I am using Jon Calhoun's Go MVC framework from github.
The framework uses julienschmidt/httprouter as its only dependency.
I have a similar main method as found in the example:
func main() {
//register routes
router := httprouter.New()
//default
router.GET("/", controllers.Login.Perform(controllers.Login.Index))
//login
router.GET("/login", controllers.Login.Perform(controllers.Login.Login))
router.POST("/login", controllers.Login.Perform(controllers.Login.PostLogin))
//dashboard
router.GET("/dashboard", controllers.Dashboard.Perform(controllers.Dashboard.Index))
//listen and handle requests
log.Fatal(http.ListenAndServe(":"+helpers.ReadConfig("port_http"), router))
}
I make a post to the login url, and it calls the following method:
func (self LoginController) PostLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params) error {
//create our api url
var url = helpers.ReadConfig("api") + "login"
//fill model to post
login := models.LoginModel{
Password: r.FormValue("password"),
Email: r.FormValue("username"),
}
//render json from model
bytes, err := json.Marshal(login)
if err != nil {
panic(err)
}
//post to the API helpers
var resp = helpers.ApiPost(url, r, string(bytes))
//check response if successful
if resp.Code != constants.ApiResp_Success {
//TODO: Handle API Errors
login.Password = ""
errors := make(map[int]string)
errors[1] = "Please provide valid credntials."
login.Common = models.CommonModel{
ErrorList: errors,
}
return views.Login.Index.Render(w, login, helpers.AcceptsGzip(r))
}
log.Println("---Redirect--")
http.Redirect(w, r, "/dashboard", 307)
log.Println("-----")
return views.Dashboard.Index.Render(w, login, helpers.AcceptsGzip(r))
}
Basically, if the login was not correct I return the same view. If the login is correct I want to redirect to another method in a different controller.
However when I call http.Redirect(w, r, "/dashboard", 307)
, it returns the following error:
http: multiple response.WriteHeader calls
I'm not sure exactly why this is happening, but I suspect that it has something to do with my listener calling the Perform function, which creates a http.handler, as shown below.
func (c *Controller) Perform(a Action) httprouter.Handle {
return httprouter.Handle(
func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
//set response headers
//TODO: set appropriate responce headers
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Cache-Control", "public, max-age=0")
w.Header().Set("Token", "NOT-A-VALID-TOKEN")
w.WriteHeader(200)
if err := a(w, r, ps); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
}
Does anyone have any idea how to redirect using this MVC framework? Or have a one off solution?
答案1
得分: 4
http.ResponseWriter的WriteHeader
方法每个HTTP响应只能调用一次,这是显而易见的原因:你只能有一个响应代码,并且只能发送一次头部。
你看到的错误意味着在同一个响应上第二次调用了它。
你的中间件调用:
w.WriteHeader(200)
然后你的处理程序也调用:
http.Redirect(w, r, "/dashboard", 307)
log.Println("-----")
return views.Dashboard.Index.Render(w, login, helpers.AcceptsGzip(r))
在响应的命运确定之前,你的中间件不应该调用WriteHeader
。
此外,不了解你使用的特定MVC框架,似乎在发送307
状态之后,你还告诉MVC框架渲染一个响应,这可能会再次调用WriteHeader
。
英文:
http.ResponseWriter's WriteHeader
method can only be called once per HTTP response, for obvious reasons: You can only have a single response code, and you can only send the headers once.
The error you see means that it is called a second time on the same response.
Your middleware calls:
w.WriteHeader(200)
Then your handler also calls:
http.Redirect(w, r, "/dashboard", 307)
log.Println("-----")
return views.Dashboard.Index.Render(w, login, helpers.AcceptsGzip(r))
Your middleware should never call WriteHeader
, until after the fate of the response is known.
Further, without knowing about your particular MVC framework, it seems possible that after you send the 307
status, then you also tell the MVC framework to render a response, which may also call WriteHeader
again.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论