HTTP Method spoofing in Go

 

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:

  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.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.

Not sure how to structure your Go web application?
My new book guides you through the start-to-finish build of a real world web application in Go — covering topics like how to structure your code, manage dependencies, create dynamic database-driven pages, and how to authenticate and authorize users securely.