© Christopher Pitt and Joe Mancuso 2020
C. Pitt, J. MancusoThe Definitive Guide to Masonitehttps://doi.org/10.1007/978-1-4842-5602-2_8

8. Creating Middleware

Christopher Pitt1  and Joe Mancuso2
(1)
Cape Town, South Africa
(2)
Holbrook, NY, USA
 

So now that we know what middleware is, let’s walk through how middleware is created.

Let’s create a middleware and then talk about each section of the middleware. We’ll keep this simple for now and just create a simple Hello World middleware and talk through it. Then we’ll get into the more complex middleware for our specific application.

If you haven’t yet caught on, we will be using a craft command for this:
$ craft middleware HelloWorld

This will create a HelloWorldMiddleware.py file for you inside the app/http/middleware directory.

There is nothing special about this directory, so you can move your middleware out of this directory if you like. Just make sure any imports in your config/middleware.py file point to the new location. This is more background information, so don’t feel like you need to move them though; this directory is fine.

Constructing Middleware

If we look at this class, you can tell that middleware is an extremely simple class with three parts. Let’s go through each part so we know what each is doing and what can be done with it.

It’s important to note as well that HTTP middleware and route middleware are constructed exactly the same. The only thing that makes it an HTTP or route middleware is how we register it with Masonite, which we will talk about in an upcoming section.

Initializer

class HelloWorldMiddleware:
    def __init__(self, request: Request):
        self.request = request
    # ...

The initializer is a simple __init__ method like any other class. The only thing special about it is that it is resolved by the container. So you can type hint application dependencies in your __init__ method, which will resolve classes much like your controller methods.

Since a lot of middleware requires the request class, Masonite will type hint the request class for you. If your specific middleware doesn’t need it, then you can remove it without issues.

The before Method

class HelloWorldMiddleware:
    #...
    def before(self):
        print("Hello World")

The before method is another simple method. Any code in this method will be responsible for running before the controller method is called. In the built-in auth middleware, this is what method is used to check if the user is authenticated and tells the request class to redirect back.

This method can also accept variables that we can pass in from our routes file. We will talk about this later on in the chapter.

The after Method

class HelloWorldMiddleware:
    #...
    def after(self):
        print('Goodbye World')

The after method is very similar to the before method except the code is ran after the controller method is called. This is where the logic would go if we wanted to minify the HTML response as an example.

This method can also accept variables that we can pass in from our routes file. We will talk about this later on in the chapter.

Registering Middleware

Now that we created our middleware class, we can register it with Masonite. We can import it into our config/middleware.py file and put it into one of two lists. We can put it in the HTTP_MIDDLEWARE list or the ROUTE_MIDDLEWARE dictionary.

HTTP Middleware

Remember earlier we said that both middleware are constructed the same, so if you want the middleware to run on every request, put it in the HTTP_MIDDLEWARE class .

This would look something like
from app.http.middleware.HelloWorldMiddleware import
HelloWorldMiddleware
HTTP_MIDDLEWARE = [
    LoadUserMiddleware,
    CsrfMiddleware,
    ResponseMiddleware,
    MaintenanceModeMiddleware,
    HelloWorldMiddleware, # New Middleware
]

Notice HTTP middleware is just a list, so you can just append it to the list. The order of your middleware may not matter, but it actually might.

The order the middleware is run is the same order you put it in the list. So the LoadUserMiddleware will run first and then the HelloWorldMiddleware will run last. Since our HelloWorldMiddleware just prints some text to the terminal, we can add it to the bottom of the list since it doesn’t really depend on anything.

On the other hand, if the middleware relied on the user, then we should make sure our middleware is after the LoadUserMiddleware. This way the user is loaded into the request class and then our middleware has access to it. As you can tell, the LoadUserMiddleware is first for exactly that reason.

Now the HTTP middleware is fully registered with Masonite and it will now run on every request. In a bit, we will see what the output will look like. Before we do that, we’ll talk about how to register route middleware.

Route Middleware

Now route middleware again is the same as HTTP middleware, but registering it is a bit different. Right off the bat we can notice that the route middleware is a dictionary. This means we need to bind it to some key.

This key is what we will use to attach the middleware to our routes. We want to name this middleware something short and sweet. We can use the key helloworld as the key and make the middleware the value in the dictionary. This will look something like
from app.http.middleware.HelloWorldMiddleware import
HelloWorldMiddleware
ROUTE_MIDDLEWARE = {
    'auth':  AuthenticationMiddleware,
    'verified': VerifyEmailMiddleware,
    'helloworld': HelloWorldMiddleware,
}

The naming convention is up to you, but I like to try to keep it to one word. If you need to split into more than one word , we can name it something like hello.world or hello-world. Just be sure not to use the : character since Masonite will splice on that key in our routes file. You’ll see more of what that means in the route section in a little bit.

Using the Middleware

So we have talked about what to use middleware for, the different types of middleware we can create, how to create both of those middleware, and finally how to register both of them with Masonite.

Now we will finally get to how we can use the middleware we created. Now the HTTP middleware, which is the one we put inside the list, is already ready to go. We don’t actually have to do anything further.

If we start navigating our application and open our terminal, then we may see something that looks like
hello world
INFO:root:"GET /login HTTP/1.1" 200 10931
goodbye world
hello world
INFO:root:"GET /register HTTP/1.1" 200 12541
goodbye world
hello world
INFO:root:"GET /dashboard HTTP/1.1" 200 4728
goodbye world

Notice we start seeing the hello world and goodbye world print statements before and after our controller methods are hit.

Route middleware on the other hand is a bit different. We will need to use this middleware by specifying the key in our routes file.

For example, if we want to use the helloworld middleware we made earlier, we can add it to a route that looks something like
Get('/dashboard', 'YourController@show').middleware('helloworld')

This will now run the middleware ONLY for this route and not for any other routes.

Looking back to our previous terminal output, our new application will look something like this:
INFO:root:"GET /login HTTP/1.1" 200 10931
INFO:root:"GET /register HTTP/1.1" 200 12541
hello world
INFO:root:"GET /dashboard HTTP/1.1" 200 4728
goodbye world
Notice we only put the middleware on the /dashboard route, so therefore it will only be executed for that specific route:
Get('/dashboard',
'YourController@show').middleware('helloworld:Hello,Joe')

Remember before we say to make sure your middleware aliases don’t have a : in the name because Masonite will splice on that? Well this is what that meant. Masonite will splice on the : character and pass all variables after it to the middleware.

Now that we said we are passing these values to the middleware, let’s look at what the middleware will look like:
class HelloWorldMiddleware:
    #...
    def before(*self*, *greeting*, *name*):
        pass
    def before(*self*, *greeting*, *name*):
        pass

Whatever we pass into the route, BOTH the before and after middleware need those two parameters.

As you might have guessed, the parameters are in the same order as you specify them in the routes. So greeting will be Hello and name will be Joe.

Middleware Stacks

Middleware stacks are another simple concept. There will be times when some of your routes look very repetitive with the same middleware over and over again. We can group middleware into middleware “stacks,” or lists of middleware, in order to run all those middleware under one alias.

For example, let’s say we have some middleware that we want to run all under a single alias. Just so we can use a better example, we may see ourselves using very similar middleware over and over again:
ROUTES = [
(Get('/dashboard', 'YourController@show')
    .middleware('auth', 'trim', 'admin')),
(Get('/dashboard/user', 'YourController@show')
    .middleware('auth', 'trim', 'admin')),
]
Notice the middleware seems a bit repetitive. What we can do here is create a middleware stack to group them. This looks like
ROUTE_MIDDLEWARE = {
    'auth':  AuthenticationMiddleware,
    'verified': VerifyEmailMiddleware,
    'dashboard': [
        AuthenticationMiddleware,
        TrimStringsMiddleware,
        AdminMiddleware,
    ]
}
We can then refactor our routes a bit to use this stack:
ROUTES = [
(Get('/dashboard',  'YourController@show')
    .middleware('dashboard')),
(Get('/dashboard/user',   'YourController@show')
    .middleware('dashboard')),
]
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.111.9