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.


With the release of Go 1.22 there’s an easier way to restrict HTTP methods. Find out in the new post.

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 these tutorials too...

Understanding Go's Empty Interface
Tue, Sep 12, 2023

Understanding Go's Empty Interface

Recently, while working with the Twilio Lookup API, I had the opportunity to properly learn about Go’s empty interface. During the process, I realised that I didn’t understand it properly, so took the opportunity to do so. Today, I am going to step through what it is and how simple they are to work with.

Go Interfaces Make Development and Testing Easier
Fri, Jul 21, 2023

Go Interfaces Make Development and Testing Easier

Substitutability or the Liskov Substitution Principle (LSP) is a concept that I’ve tried to adhere to for some years when writing code. It’s beneficial for many reasons, but particularly when testing, as it can indirectly force you to write code that is more testable. Recently, I’ve started appreciating how it works in Go, and will step through how in this short article.

Restrict HTTP Request Methods in Go 1.22
Thu, Apr 11, 2024

Restrict HTTP Request Methods in Go 1.22

Restricting HTTP request methods, when working with net/http prior to Go 1.22 was a little complicated. However, that’s all changed. Now, it’s pretty trivial. In this short tutorial, I’ll show you the new approach.

Go mod tidy - A Quick Introduction
Fri, Apr 5, 2024

Go mod tidy - A Quick Introduction

What is go mod tidy and why you would use it? In this short tutorial you’ll get a brief introduction to what it is and see the essentials of how to use it.


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