CodeX
Published in

CodeX

Creating Middleware with httprouter

Photo by Jametlene Reskp on Unsplash

When working with HTTP-based services in Go, many developers (including myself) like to use HTTP Multiplexers (mux). Multiplexers, also sometimes referred to as Request Routers, offer a suite of features and capabilities. The simplest form allows users to register a specific handler function with specific URL patterns and methods.

One of the more advanced features is using HTTP Multiplexers to create “HTTP middleware.” This capability allows users to create a standard function or set of executed functions before requests route to the User-defined handlers. Developers can use these common functions to perform many tasks, from creating a standard logger to handling authentication.

Today’s article will explore using the httprouter HTTP Multiplexer to create a simple HTTP middleware.

Why httprouter

There are many Multiplexers available, with popular ones being Gorilla Mux, httprouter, and Bone. For this article, I will explore httprouter, one I often use because I like how simple and efficient it is. But also because there is a version available for both net/http and fasthttp. I use both of these HTTP packages throughout my various projects. And having one HTTP Multiplexer to use regardless of which I'm using helps keep things simple.

Getting Started with httprouter

Before jumping into creating an HTTP middleware function, we should first start with the basics of using httprouter. We can look at the most basic HTTP service that prints “Hello World” to kick us off.

The above code only uses the standard net/http and the default mux with the standard library. One of the nice things about Go is that the standard library is all you need if you build elementary HTTP-based services.

But for this article, we want to do something more advanced; we want to have a standard HTTP middleware executed before our HTTP handler.

To get started, let’s add httprouter into the mix.

As we can see from the code example above, we made a few changes to our code. The first to call out is rather than using http.HandleFunc() to register our handler function, we created an httprouter.Router and used this to register our handler function for the /hello end-point.

A keen eye may also notice that with httprouter, we can register our functions under different HTTP Methods. This feature is one of the excellent features of httprouter over the default standard library. With httprouter, users can register different functions for different HTTP Methods. In our example, it doesn’t make sense to serve the /hello route under any method other than a GET.

In addition to the changes in how we register our handler function, it is worth calling out that the signature of our handler function has also changed. Now, in addition to the standard http.ResponseWriter and the http.Request types, our function also receives an httprouter.Params type.

With httprouter, one of the more advanced features is the ability to parameterize the URI. For example, instead of registering /hello, we could register /hello/:name. We could ask users to put their names within the URI, such as /hello/ben. We could then access that parameter using the httprouter.Params provided to our handler.

We now have a fully working HTTP service using httprouter; we can start creating our middleware function from here.

Creating our HTTP Middleware

For this article, we will create a basic logger middleware. This middleware aims to create a consistent log of HTTP requests regardless of what HTTP handler executes. That means before our handler() function executes, we want our middleware to run first.

We will achieve this by creating a wrapper function and then including this wrapper function with our handler() registration call.

In the above, we can see our wrapper function middleware() is now being registered along with the handler() function. The name of this technique is chaining; by wrapping the handler() function with the middleware() function, we are chaining the two functions together. The middleware() function executes first, and at the end, the middleware() function itself calls our handler() function.

At this point, we have a working HTTP middleware powered by httprouter. For every custom handler we have, we can use this HTTP middleware to keep a consistent log. But what about non-custom handlers? What about things like PProf that has their handlers built with the standard HTTP signature?

Registering non-Custom Handlers with the HTTP middleware

A common practice for libraries such as PProf is to provide HTTP handlers as part of the package interface. The net/http/pprof package has several functions that adhere to the standard net/http method signature. Functions such as Index, which handles the /debug/pprof/ end-point, and Profile, which serves the /debug/pprof/profile CPU profiling end-point.

The problem is that these functions only take two inputs, http.ResponseWriter and http.Request. They don't match our HTTP middleware's signature, but we still want to use our middleware for these functions.

Luckily, we can. Using the http.HanderFunc() adapter we can convert these functions into http.Handler's. And from there, we can use another small wrapper to wrap this http.Handler with our middleware.

As we can see from the code above, we added a wrapper() function with an input of http.Handler, and that wrapper function is wrapping an anonymous function running http.Handler.ServeHTTP() with our middleware.

With the addition of this wrapper() function, we can now introduce our HTTP middleware on any standard HTTP handler, whether it's from PProf or other libraries.

Now that we have our wrapper() function defined, let's go ahead and add the rest of the PProf parts.

In the above example, a keen eye may notice that about halfway through registering /debug/pprof paths, we stop using the http.HandlerFunc() adapter. Instead, we are registering a pprof.Handler() function with various arguments. Some paths are satisfied with the PProf package via the pprof.Handler() function, which returns an http.Handler type.

Summary

With the above, we have a simple hello world application that shows how easy it is to create HTTP Middleware functions with httprouter. We can also see how it is possible to use this middleware even with packages like PProf, where the HTTP handlers use a different signature.

If you build a complex service with many routes and different handlers, routers such as httprouter can be a great tool. But the best thing about Go is, if all you need to do is simple HTTP services, you don’t need much beyond the standard library.

Originally published at https://bencane.com.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Benjamin Cane

Benjamin Cane

422 Followers

Distinguished Engineer @AmericanExpress building payments systems. Author: https://amzn.to/3kuCFpz, Thoughts, & Opinions are my own.