Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go web server is automatically redirecting POST requests

Tags:

http

go

I've been trying to solve a weird problem for quite some time now. After stepping through lots of angular code, I noticed something weird while logging requests to my server through Charles.

When I post to a url /myurl the request never actually hits my server. Instead, it gets a 301 response and THEN a GET request hite my server.

This is incredibly puzzling. Has anyone else run into this problem? I've uploaded a screenshot of my Charles log incase you are interested.

enter image description here

Just as a reference, this is what my server looks like:

type FormStruct struct {
    Test string
}

func PHandler(w http.ResponseWriter, r *http.Request) {
    var t FormStruct

    req, _ := httputil.DumpRequest(r, true)

    log.Println(string(req))
    log.Println(r.Method) // GET
    log.Println(r.Body)

    decoder := json.NewDecoder(r.Body)
    err := decoder.Decode(&t)
    log.Println("Decoding complete")
    if err != nil {
        log.Println("Error")
        panic(err.Error()+"\n\n")
    }
    log.Println(t.Test)

    w.Write([]byte("Upload complete, no errors"))
}

func main() {
    http.HandleFunc("/myurl/", PHandler)    
    fmt.Println("Go Server listening on port 8001")
    http.ListenAndServe(":8001", nil)
}
like image 549
dopatraman Avatar asked Mar 30 '16 18:03

dopatraman


1 Answers

The explanation is simple: because you used the "/myurl/" path when you registered your PHandler (note the trailing slash /!) but you directed your browser to /myurl (note there is no trailing slash). And by default the http package implementation will perform (send back) a redirect request so if the browser follows it (it will), the new URL will match the registered path.

This is documented at type http.ServeMux:

If a subtree has been registered and a request is received naming the subtree root without its trailing slash, ServeMux redirects that request to the subtree root (adding the trailing slash). This behavior can be overridden with a separate registration for the path without the trailing slash. For example, registering "/images/" causes ServeMux to redirect a request for "/images" to "/images/", unless "/images" has been registered separately.

Should you direct your browser directly to /myurl/, you would not experience a redirect.

Or if you don't need to handle a rooted subtree but only a single path (e.g. /myurl), then register your handler only to this single path:

http.HandleFunc("/myurl", PHandler)

And then of course direct your browser to /myurl, and you will not experience any redirect either.

...Or as the documentation suggests: register both paths to your handler if you really need it:

http.HandleFunc("/myurl", PHandler)
http.HandleFunc("/myurl/", PHandler)

And now no matter which path you call (/myurl or /myurl/), both will result in calling your handler without any redirection taking place.

Notes:

In your situation when a redirect was sent back to the browser, the browser will not repeat the POST request (but rather just a "simple" GET request).

Generally speaking a browser will not send POST data to a redirect URL because the browser is not qualified to decide if you're willing to send the same data to the new URL what you intended to send to the original URL (think about passwords, credit card numbers and other sensitive data). But don't try to circumvent it, simply use registered path of your handler to POST to, or any of the other tips mentioned above.

You can read more on the subject here:

Why doesn't HTTP have POST redirect?

like image 112
icza Avatar answered Sep 28 '22 15:09

icza