重定向返回http:多个response.WriteHeader调用

huangapple go评论99阅读模式
英文:

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.ResponseWriterWriteHeader方法每个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.

huangapple
  • 本文由 发表于 2017年8月25日 03:41:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/45869688.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定