Back to Home

Go Middleware - Part 2

  • February 9, 2019 |
  • 3 min read

In this second part of the Go middleware tutorial series, we'll cover a recursive approach that provides a couple benefits beyond the simple loop chain example from part 1, including:

  1. the ability to wrap around the next handler in the chain (i.e. add logic before and after the subsequent handlers are applied)
  2. the ability to short-circuit the request chain in any middleware handler (e.g. if authentication fails)

Part 2 - Recursive Chain Middleware Example

Photo by Szabo Viktor

The code for this tutorial can be found here:
https://github.com/benjohns1/go-middleware-example/blob/master/recursivechain/main.go

Step 1 - Simple Server

Just as we did in part 1, let's start with a simple working HTTP server:

main.go:

Run the server:

go run main.go

Test in a browser:

localhost:8080/api/v1

Notice the businessLogic function returns a closure around the actual http handler, so we can pass in an all-important config value "12345" when the server is started.

Step 2 - Recursive Chain

Rather than use a simple loop that calls handlers one after another like we did in part 1, instead we're going to pass the second handler as a parameter to the first handler, pass the third handler as a parameter to the second handler, and so on:

firstHandler(secondHandler(thirdHandler(fourthHandler())))

Each handler will then be responsible for calling (or not!) the next one in the chain. We can do better than a bunch of eye-bleeding nested functions and use recursion:

main.go (partial):

The middlewareFunc type describes the new type of middleware function we need to pass into the chain function. It accepts the next handler in the chain and returns the current handler, which wraps the next handler (this should make more sense in a bit).
The chain function just provides the nice variadic parameter syntax and converts the final handler function into the http.Handler interface that the http router accepts.
The recurseChain function does the actual work of sending the next handler as a parameter to the current one recursively, up until there aren't any more handlers, and it finally returns an empty function to complete the chain.

To use this chain function, modify the businessLogic to:

  1. wrap the http handler in a new closure that passes-in the next handler
  2. and call the next handler where appropriate

main.go (partial):

Now our businessLogic function accepts the same config parameter, but it returns the middlewareFunc type function that chain() accepts. This middlewareFunc accepts the next handler in the chain as a parameter and returns the actual handler function that processes the request. The actual handler function has access to the outer config and handler parameters via closures; it can call next(w, r) to continue to the next handler in the chain. In this case next(w, r) just calls the empty function returned from the last iteration of recurseChain.

Step 3 - Wrap and Short-Circuit

Okay, thanks a lot, now we've just made things more complicated! Wait, wait, we should KISS. We're adding complexity because it gives us the ability to actually wrap our nested handlers in functionality like this:

main.go (partial):

and short-circuit the request chain like this:

main.go (partial):

The logger middleware prints an entry before and after all subsequent handlers run. The shortCircuitLogic randomly decides not to call any of the subsequent handlers in the chain by returning without calling the next function.

As you can see this provides a lot of flexibility with our middleware, while still keeping each function decoupled from the others.

In Part 3 we'll look at a common variant of this approach that doesn't require the final handler function to call an empty handler.

Final Server Code

main.go:

Go Middleware - Part 1

Go Middleware - Part 3

Related Posts

The essential design concepts I use when developing an evolvable, distributed system.

Read More

How can we continuously integrate small changes while practicing acceptance test-driven development?

Read More

TDD and Testing Behavior

January 24, 2024

The importance of testing behavior when using test-driven development

Read More

When is it appropriate to use centralized orchestration versus event-driven choreography?

Read More

When defining a business problem and planning its solution, keep the two conversations separate...

Read More

Modern message brokers provide many important benefits to a distributed system...

Read More

Printable cheat sheets to help remember some of Uncle Bob's valuable contributions to the industry

Read More

Why Terraform?

December 25, 2019

Terraform leads the way in the infrastructure-as-code world...

Read More

I was looking for a quick and easy way to put together a personal static site and...

Read More

A few weeks ago, I decided to try Svelte's Sapper framework to handle the front-end of a simple app...

Read More

After years of consulting, I find myself continually coming back to three basic principles of system design...

Read More

In this fifth and final part of the Go middleware tutorial series, we'll use what we've learned to create a more structured API example...

Read More

Go Middleware - Part 4

February 24, 2019

In this fourth part of the Go middleware tutorial series, we'll discuss passing custom state along the request chain.

Read More

Go Middleware - Part 3

February 15, 2019

In this third part of the Go middleware tutorial series, we'll quickly look at a common variant on the recursive middleware implementation from part 2.

Read More

Go Middleware - Part 1

February 6, 2019

This is the first in a series of simple tutorials explaining the usage of HTTP middleware in Go.

Read More

How do we manage the architectural complexity that inevitably arises from using cloud services?

Read More

This Old Blog

January 20, 2019

I've decided to resurrect this old blog to publish some nuggets about software architecture and development, and perhaps...

Read More

Drupal 6 Theme Info Error

September 14, 2011

Recently one of my client sites had an issue where the custom theme info was corrupted...

Read More

Here's a slight modification to the handy Google Bookmarks Bookmarklet...

Read More

While building a Drupal site for one of my clients, I was having a heck of a time integrating...

Read More