Restrict Allowed Route Methods in Go Web Apps

Restrict Allowed Route Methods in Go Web Apps

It’s incredibly easy to build web apps and APIs using Go’s net/http package. All you really need to do is to specify a route and its handler. But, how do you restrict the methods that routes can accept? In this short tutorial, I’ll show you how.


Recently, I’ve been building a small encryption/decryption API with Go. It’s been going well, giving me loads of ways to continue growing my Go skills, and to learn about encryption.

However, I’d been wondering (pretty much right from the get go) how I could restrict the allowed HTTP request methods for the API’s routes. I’d had a bit of a look through net/http, after finding that HandleFunc() only takes a route pattern and a handler function.

Unfortunately, I couldn’t find anything. This seemed odd, as I’m used to being able to define the supported method(s) when defining the route and its handler, in PHP.

$app->get('/', RouteHandler::class);
$app->post('/album', RoutePostHandler::class);
$app->put('/album/:id', RoutePutHandler::class);
$app->route('/album/:id', RouteAnyHandler::class, ["get", "post"]);

Take the example code above, used in the Mezzio framework. It defines four routes; (their handlers aren’t important). The first three are only allowed to be requested with GET, POST, and PUT respectively. The fourth, however, can be requested with either GET or POST.

The benefit of this approach is that you only need to look in one place to find a route’s allowed methods: the routing table definition.

Where do you do this with net/http? I wasn’t, yet, sure. Take a look at the small code example below, for example.

package main 

import (
	"io"
	"log"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
		io.WriteString(writer, "Here is a response.\n")
	})

	log.Print("Starting server on :4000")
	err := http.ListenAndServe(":4000", mux)
	log.Fatal(err)
}

You can see that the app has a single route, /, handled by a closure, which writes “Here is a response.” as the response’s body. Nothing special about that. Right? Given that, requests can use any HTTP method, and the same response will be returned.

So how do you do restrict the allowed methods with net/http?

If, for example, the handler was processing a form, you’d restrict the allowed methods to just POST. What’s more, if users made requests with other methods, out of courtesy (and in compliance with the spec), you’d return an Allow header telling the user that the route only supports the POST method.

Gladly, doing both is pretty trivial. You’d refactor the closure, or your function if you weren’t using a closure, similar to following:

mux.HandleFunc("/", 
    func (writer http.ResponseWriter, request *http.Request) {
    if request.Method != "POST" {
        writer.Header().Set("Allow", "POST")
        http.Error(
            writer, 
            "That method is not allowed.", 
            http.StatusMethodNotAllowed,
        )

        return
    }
    io.WriteString(writer, "Here is a response.")
})

The if statement checks if the request’s method is POST. If not, it does three things:

  1. Sets the Allow header to POST.
  2. Sets the body of the response to That method is not allowed..
  3. Sets the response’s status code to 405 Method Not Allowed.

The last two steps are done by the http.Error() utility method. It’s a little time-saver that I came across last week. It simplifies setting the response’s status code and body, avoiding the need to use http.ResponseWriter directly.

Here’s an example of what you’d see if you made a GET request to the route with curl.

HTTP/1.1 405 Method Not Allowed
Allow: POST
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Fri, 01 Sep 2023 08:53:47 GMT
Content-Length: 28

That method is not allowed.

That’s how to restrict the allowed methods for a route with Go

Sure, doing so in Go isn’t what I’m used to when using PHP frameworks, such as Slim, Mezzio, or Laravel. And, I’d love to be able to set the allowed methods when defining the route. But, the approach is clear and succinct, and takes only a little bit of extra effort.

Do you use it? If not, what’s your approach? I’d love to learn from you too. Share your approach and thoughts in the comments.


You might also be interested in...


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.


Join the discussion

comments powered by Disqus