HTTP Method Spoofing in Go

A.K.A. HTTP method overriding.

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 you need to resort to either sending 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' 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:

  1. Intercept POST requests before they reach any application handlers.
  2. Check for a spoofed HTTP method, either in a _method parameter of the request body or a X-HTTP-Method-Override header.
  3. If a spoofed method exists — and is equal to "PUT", "PATCH" or "DELETE" — the current http.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.Println(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)
    }
}