Middleware, in the broadest sense of the word, is the term used in software architecture to describe software that sits between other software systems enabling them to work together.
In PHP web frameworks, we can think of middleware as being the series of layers that an HTTP request (on the way in) and an HTTP response (on the way out) passes through. The application itself, with all the business logic, lives at the very centre…
A typical held (simplified) mental model of the HTTP request/response lifecycle is as follows:
a HTTP request object is created from PHP global variables
routing maps the request to a controller
the controller dispatches the request to a service (or ALL THE CODE IS HERE)
the service does some work and passes the output back to the controller
the controller generates and returns a HTTP response (with the body usually being passed through a templating engine)
That's all well and good… but some requests that often get handled at the controller level really don't need to be. In fact, in my opinion, they should never, ever arrive there, they can (and should) be handled with middleware.
Typically, middleware is used for, but not limited to, performing the following tasks:
modifying an incoming HTTP request before it reaches application land
handling an incoming HTTP request at the boundary of application land
modifying an outgoing HTTP response
In this article, we'll focus on handling an incoming HTTP request at the boundary of application land… let's take a look at one particular scenario where using middleware in a PHP web application makes perfect sense.
The OAuth2 authorization code grant
The OAuth2 authorization code grant is the most well known of all the OAuth2 grant types: it provides a way for a user who has data in one application to grant a second application access to that data without having to share credentials between the two applications.
For the purposes of this post, let's say we're building a web application in Laravel 7 called Striva! Striva is fantastic! It connects with Strava (a platform for recording and tracking exercise activities) and sends the user motivational messages when they're slacking…
Where are your running shoes? Nope, you can't remember, can you?
The gym just called to see if you're OK, don't worry! I told them they had the wrong number…
Congratulations, your bike has been successfully listed for sale!
It's really great!
To enable this wonderful web application to work, Striva requires access to the user's Strava data, the OAuth2 authorization code grant is perfect for this use case. Here's what the user journey looks like:
The first step, a user signs up for Striva…
When the user clicks the Authorize Striva button, they're taken to Strava where they're presented with a summary of the data Striva requires access to…
On clicking the Authorize button, Strava will redirect them back to Striva with an authorization code in the URL.
Behind the scenes, Striva then exchanges that authorization code with Strava for an access token and all being well, will forward the user back to their Striva dashboard and the user journey is complete.
That's the happy path.
If, however, the user clicks Cancel, they'll be redirected back to Striva with an access denied error message in the URL.
Now, we could allow this request to pass into our application layer and handle it in the controller… with a bunch of nested conditionals, come on, admit it, we've all done it! But let's instead create middleware to take care of this request, let's see how we can neatly separate the handling of a simple HTTP request from our application and keep our application clean and clear of such requests.
Creating middleware in Laravel 7
Laravel provides an artisan command that generates a skeleton class that can be used as a middleware starting point. Let's use the make: middleware command to generate three middleware, one called StravaOAuthCallbackError, another called StravaOAuthCallbackCode and finally, StravaOAuthAccessToken.
$ php artisan make:middleware StravaOAuthCallbackError
$ php artisan make:middleware StravaOAuthCallbackCode
$ php artisan make:middleware StravaOAuthAccessToken
Laravel places middleware in the app/Http/Middleware directory, let's have a look at the StravaOAuthCallbackError middleware that was generated…
It's easy to see that the handle method simply invokes the next middleware, passing the request object along… This middleware, as-is, doesn't do anything yet but let's go ahead and register it.
In app/Http/Kernel.php, we can add either a single middleware to the $middleware array or we can create what's called a middleware group in $middlewareGroups. We'll go ahead and register a new group as that makes it easier for us to attach multiple middleware to a route:
And attach the Strava middleware group to our Strava OAuth callback route…
Note that the above code assumes that we've already created a StravaOAuthController with a callback method. This would be where we'd handle the persistence of the access and refresh tokens which we'd get back in the response from Strava.
Now, let's finally add some code to our middleware!
Yay, a small class, doing very little. How refreshing!
And here's StravaOAuthCallbackCode…
Okay, so clearly we could roll up these simple checks into a single middleware but for me, the clarity we get from separating out things like this makes a lot of sense!
And now, let's take a look at the third middleware, StravaOAuthAccessToken is responsible for POST'ing the authorization code back to Strava…
For simplicity, I've deliberately left out configuration (client_id and client_secret could be stored in /config/strava.php and accessed with the config facade) and exception handling: I'd expect at least a try around the POST request to catch any exceptions (Illuminate\Http\Client\ConnectionException) that occur.
I hope this article has done enough to convey the advantages of handling certain HTTP requests at the boundary of an application, that middleware is a super mechanism that promotes separation of concerns (HTTP land and APPLICATION land) and it leaves you inspired about introducing middleware into your web applications if you don't already.
In the next article, we'll look more closely at the popular middleware implementations, understand how they work and discuss some of the other tasks that can be done using middleware. In the meantime, the Laravel documentation for middleware can be found at: https://laravel.com/docs/7.x/middleware