As a web developer you probably already know that HTML forms only support the GET
and POST
HTTP methods.
If you want to send a PUT
, PATCH
or DELETE
request from your web application, you need to either send a XMLHttpRequest
from JavaScript (where they are supported by most major browsers) or implement a workaround in your server-side application code to support 'spoofed' or 'overridden' HTTP methods.
The de-facto workaround — which you might be familiar with if you've used frameworks like Ruby on Rails, Laravel or Express — is to include a hidden _method
input in your form containing the spoofed HTTP method. A bit like this:
<form method="POST" action="/">
<input type="hidden" name="_method" value="PUT">
<button type="submit">Submit</button>
</form>
Another common workaround is to send a spoofed HTTP method in a X-HTTP-Method-Override
header.
So how can we support these things in a Go application?
MethodOverride Middleware
Intercepting and dealing with spoofed HTTP methods is the perfect task for some custom middleware. We want the middleware to:
- Intercept
POST
requests before they reach any application handlers. - Check for a spoofed HTTP method, either in a
_method
parameter of the request body or aX-HTTP-Method-Override
header. - If a spoofed method exists — and is equal to
"PUT"
,"PATCH"
or"DELETE"
— the currenthttp.Request.Method
value should be updated accordingly.
It's pretty quick to implement:
package main
import (
"net/http"
)
func MethodOverride(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Only act on POST requests.
if r.Method == "POST" {
// Look in the request body and headers for a spoofed method.
// Prefer the value in the request body if they conflict.
method := r.PostFormValue("_method")
if method == "" {
method = r.Header.Get("X-HTTP-Method-Override")
}
// Check that the spoofed method is a valid HTTP method and
// update the request object accordingly.
if method == "PUT" || method == "PATCH" || method == "DELETE" {
r.Method = method
}
}
// Call the next handler in the chain.
next.ServeHTTP(w, r)
})
}
You can then use the middleware in your application like so:
package main
import (
"html/template"
"io"
"log"
"net/http"
)
const form = `
<!DOCTYPE HTML>
<html>
<body>
<form method="POST" action="/">
<input type="hidden" name="_method" value="PUT">
<label>Example field</label>
<input type="text" name="example">
<button type="submit">Submit</button>
</form>
</body>
</html>
`
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", formHandler)
// Wrap the servemux with the MethodOverride middleware.
err := http.ListenAndServe(":4000", MethodOverride(mux))
log.Print(err)
}
func formHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
t, err := template.New("form").Parse(form)
if err != nil {
http.Error(w, err.Error(), 500)
}
t.Execute(w, nil)
case "PUT":
io.WriteString(w, "This is a PUT request")
default:
http.Error(w, http.StatusText(405), 405)
}
}
If you enjoyed this post...
You might like to check out my other Go tutorials on this site, or if you're after something more structured, my books Let's Go and Let's Go Further cover how to build complete, production-ready web apps and APIS with Go.