Golang: 重定向两次导致 http: 多次调用 response.WriteHeader

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

Golang: redirect twice and cause http: multiple response.WriteHeader calls

问题

我有一个非常奇怪的输出...让我先贴出我的代码,然后再解释一下:

在主函数下,我声明了:

manageMux.HandleFunc("/info", info)

首先,我登录并从"/login"重定向到页面"/":

func login(w http.ResponseWriter, r *http.Request) {
    if r.Method == "GET" {
        t, err := template.ParseFiles("manage/login.html")
        checkError(err)
        t.Execute(w, nil)
    } else { //POST
        r.ParseForm()
        //在这里进行一些身份验证
        http.Redirect(w, r, "/", http.StatusFound)
    }
}

然后,我从当前页面"/"(只有按钮)重定向到另一个页面"/info":

func manage(w http.ResponseWriter, r *http.Request) {
    t, err := template.ParseFiles("manage/manage.html")
    checkError(err)
    t.Execute(w, nil)
    r.ParseForm()
    if r.Form["Button"] != nil { //只获取来自按钮的POST操作
        if r.Form["Button"][0] == "Log" {
            http.Redirect(w, r, "/info", http.StatusFound)
        }
    }
}

最后,我创建了一个模板,并希望在客户端显示:

const tpl = `stuff inside`

type InfoDefault struct {
    //stuff inside
}

func info(w http.ResponseWriter, r *http.Request) {
    info := InfoDefault{
        //stuff inside
    }

    t, err := template.New("info").Parse(tpl)
    checkError(err)
    err = t.Execute(os.Stdout, info)
    checkError(err)
}

现在,奇怪的是,当我点击页面"/"上的按钮时,我得到了错误"http: multiple response.WriteHeader calls"。同时,在我的页面底部出现了一个名为"found"的链接(奇怪!),当我点击"found"链接时,我得到了所有解析后的模板在服务器端而不是网页上打印出来。

有人知道为什么吗?如何修复错误并在客户端网页上打印内容?谢谢!

英文:

I have a very weird output ... let me post my code first then I will explain:

Under main function I declared

manageMux.HandleFunc("/info", info)

first I log in and redirect from "/login" to page "/":

func login(w http.ResponseWriter, r *http.Request) {
    if r.Method == "GET" {
	    t, err := template.ParseFiles("manage/login.html")
	    checkError(err)
	    t.Execute(w, nil)
    } else { //POST
	    r.ParseForm()
	    //do some authentications here
	    http.Redirect(w, r, "/", http.StatusFound)
    }
}

Then I redirect to another page "/info" from current page "/" (which has only buttons):

func manage(w http.ResponseWriter, r *http.Request) {
    t, err := template.ParseFiles("manage/manage.html")
    checkError(err)
    t.Execute(w, nil)
    r.ParseForm()
    if r.Form["Button"] != nil { //to get only POST actions from buttons
		if r.Form["Button"][0] == "Log" {
			http.Redirect(w, r, "/info", http.StatusFound)
		} 
    }
}

At last, I made a template and would like to show on client side:

const tpl=`stuff inside`

type InfoDefault struct {
    //stuff inside
}

func info(w http.ResponseWriter, r *http.Request) {
    info := InfoDefault{
	    //stuff inside
    }

    t, err := template.New("info").Parse(tpl)
    checkError(err)
    err = t.Execute(os.Stdout, info)
    checkError(err)
}

Now, the weird thing is, when I click the button on page "/", I got the error "http: multiple response.WriteHeader calls". At the same time a link called "found" shows up on the bottom of my page (weird!), and when I click the link "found", I got all my parsed template printed on the server side instead of webpage.

Does anyone know why...? And how to fix the error and print stuff on client webpage? Thank you!!!

答案1

得分: 1

正如JimB已经指出的那样:你的服务器会混淆,因为写入http.ResponseWriterredirect都有不同的状态码关联。你不能同时做这两件事。

实际上,我想进一步扩展一下,说明如何将数据传递到下一个页面(假设你正在重定向)。

Headers(头部)
你可以向请求对象写入一些信息,并在目标页面上接收它。例如:

func myHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("My-Awesome-Header", "Rocks")
    ...
}

Session(会话):
根据我所看到的,你在谈论访问控制,我认为通过会话来持久化用户数据更好。例如:你可以使用数据库或像https://github.com/gorilla/sessions这样的会话处理程序。查看这个线程:https://stackoverflow.com/questions/26106363/best-practice-with-sessions-gorilla-sessions。

Cookies(Cookie):
我不确定你在使用什么类型的前端,但在Cookie上存储非敏感数据可能是一个选择。没有什么比这个更好了(它在示例中有真正的巧克力曲奇饼;-)):https://astaxie.gitbooks.io/build-web-application-with-golang/content/en/06.1.html。

英文:

As JimB already pointed out: your server gets confused because there are different status codes associated with both writing to http.ResponseWriter and the redirect. You can't do both at the same time.

I would actually like to expand more on how you can carry data over to the next page (assuming you are redirecting).

Headers
You can write some information to the request object and receive it on the destination page. Example:

func myHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("My-Awesome-Header", "Rocks")
    ...
}

Session:
You are talking about access control as far as I can see, and I think persisting user data is better done through a session. Example: you can use a database or a session handler like https://github.com/gorilla/sessions. Check out this thread: https://stackoverflow.com/questions/26106363/best-practice-with-sessions-gorilla-sessions.

Cookies:
I'm not sure what kind of front-end you are using, but storing non-sensitive data on the cookie could be an option? Nothing beats this one (it has real choc-chip cookies in the examples Golang: 重定向两次导致 http: 多次调用 response.WriteHeader ): https://astaxie.gitbooks.io/build-web-application-with-golang/content/en/06.1.html.

答案2

得分: 0

在你的manage处理程序中,你正在执行模板,该模板将写入http.ResponseWriter并触发一个http.StatusOK(200)状态码。在此之后,你无法进行重定向,因为这需要发送一个不同的响应代码。

如果你需要重定向,请在执行模板之前进行重定向。

英文:

In your manage handler, you're executing the template which will write to the http.ResponseWriter and trigger an http.StatusOK (200) status code. You can't redirect after that, since that requires sending a different response code.

If you need to redirect, do it before executing the template.

huangapple
  • 本文由 发表于 2016年2月9日 02:12:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/35276391.html
匿名

发表评论

匿名网友

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

确定