Lot of the tutorials on the Web show different ways of writing Go HTTP middlewares, but most of them use functional approach meaning using functions that get dependencies as parameters and return HTTP handler function or handler. There is nothing wrong about this, but it can be quite messy to write a complex middlewares like database based authentication and authorization or simply middlewares which have lot of dependencies. In this article I am going to explain how to use more object oriented way of writing such middlewares. Let’s start with the basic object oriented middleware example:
Here we can use
NewMiddleware constructor to pass all dependencies like database connection pool and so on. We can use returning middleware object in several different ways. Now let’s check how we can use this middleware.
1. Wrap single endpoint
This method is intended to be used just to wrap a single handler function. If you use it multiple times on the same middleware instance, it may produce unwanted results because each call to Wrap method would override the next handler function. If you have to wrap multiple functions, please check the examples number 3, 4 and 5.
2. Handle single endpoint
In this example you can see how to use
Handle method. Again, this can be used just once similar as noted in the previous example.
3. Handle multiple endpoints
It this example we are going to use Handle method to wrap calls to all available endpoints.
As you can see, we wrap the whole multiplexer in this example so all handler functions (
http.HandleFunc) and handlers (
http.Handle) would be wrapped in the middleware. This is probably the most useful example so far and it is intended to be used for authentication, authorization and similar middlewares which are gonna be used by multiple endpoints.
4. Wrap multiple sub endpoints
Here we have an example where
/ endpoint is not wrapped by middleware and
/welcome endpoint is wrapped. Just take care that you call
Handle just once on one instance of the middleware.
5. Handle with Chi
As Chi is Go idiomatic router, I would like to show you how you can use this pattern with it.
Similar as in the example 3, we use
Handle method to apply the middleware to all further endpoints.
Once more, take care that multiple calls to
Wrap methods on the same instance of the middleware may produce unpredicted behavior because you may override the next handler. Of course, you can make your
Wrap methods panic if
next is already set in order to avoid confusion.