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.
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)
}
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?