2012 Feed

Middleware

OX - 2012-12-05

Plack Middleware

Middleware are a concept from Plack which allow you to intercept requests before they are seen by your application, and manipulate responses before they are sent back to the server. This can be used to add many different kinds of behavior to your application - for instance, redirecting users to a login page if they attempt to access the site without being logged in.

At the most basic level, middleware are just subroutines which take a PSGI application as an argument, and return a new PSGI application. In this way, you can modify the PSGI environment before calling the underlying application, fiddle with the response that the application returns before sending it back to the server, or even do something else entirely.

Plack::Middleware is a class you can inherit from which helps in writing middleware. It allows you to write the middleware the same way that you would write a PSGI application, which will typically be more natural. Plack also comes with a wide variety of different pre-written middleware, which you can use in your application directly.

The wrap keyword

The wrap keyword is how you add middleware to your OX application. wrap must be called inside a router block, and is quite similar to the enable keyword in Plack::Builder. Middleware can be specified in one of three ways:

  • A coderef


    1: 
    2: 
    3: 
    4: 
    5: 
    6: 
    7: 
    8: 
    9: 
    10: 
    11: 

     

    wrap sub {
        my $app = shift;
        return sub {
            my $env = shift;
            my $res = $app->($env);
            push @{ $res->[1] }, (
                'X-Powered-By' => "OX $OX::VERSION",
            );
            return $res;
        };
    };

     

    This will call the given subroutine with the application as the first argument, and use the return value as the new application.

  • An instance of a Plack::Middleware subclass


    1: 
    2: 
    3: 
    4: 
    5: 

     

    my $mw = Plack::Middleware::AccessLog->new(
        format => 'combined',
    );

    wrap $mw;

     

    This will call the wrap method on the object, and use the return value as the new application.

  • The name of a Plack::Middleware subclass


    1: 
    2: 
    3: 
    4: 

     

    wrap 'Plack::Middleware::Static' => (
        root => 'static_root',
        path => literal(qr{^/(?:images|js|css)/}),
    );

     

    This will first instantiate a new instance of the middleware class, and then use that instance as described in the previous bullet point. The instance will be instantiated via Bread::Board, so it can depend on services that your application has declared. The dependencies should be listed as a hash after the name of the class.

The middleware will be applied in such an order that the first wrap statement corresponds to the outermost middleware.

wrap_if

Sometimes you don't want the middleware to be in effect for every request. For instance, you may have a middleware which redirects unregistered users to a login page, but this wouldn't work if this middleware was in effect for requests for the login page itself!

wrap_if allows you to conditionally apply middleware. It takes a coderef as an additional initial parameter, and the middleware will only be applied if that coderef returns a true value. It is similar to the enable_if keyword in Plack::Builder. For instance:


1: 
2: 
3: 
4: 

 

wrap_if sub { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' },
    'Plack::Middleware::StackTrace' => (
        force => literal(1),
    );

 

The condition subroutine will be called with the PSGI environment as the first argument, and should return true if the middleware should be applied. This example will only apply the StackTrace middleware if the request is coming from localhost.

Example

Here is a more complete example of using wrap and wrap_if. This application enables the StackTrace middleware for requests coming from localhost (to make debugging easier), and serves static files from the application via the Static middleware.


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 

 

package MyApp;
use OX;

has static_root => (
    is => 'ro',
    isa => 'Str',
    value => 'root/static/',
);

has profile => (
    is => 'ro',
    isa => 'MyApp::Controller::Profile',
);

router as {
    wrap_if sub { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' },
        'Plack::Middleware::StackTrace' => (
            force => literal(1),
        );

    wrap 'Plack::Middleware::Static' => (
        root => 'static_root',
        path => literal(qr{^/(?:images|js|css)/}),
    );

    route '/profile' => 'profile.self';
    route '/profile/:user' => 'profile.view';
};

 
Gravatar Image This article contributed by: Jesse Luehrs <jesse.luehrs@iinteractive.com>