Plack::Middleware(3)

πŸ’– Plack::Middleware β€” the base class for easy-to-use PSGI middleware πŸ’–

okay bestie, this is the Plack::Middleware page from the User Contributed Perl Documentation, and it's genuinely the glow-up your PSGI stack has been waiting for. no cap. πŸ¦‹


✨ NAME ✨

Plack::Middleware β€” the base class that makes writing PSGI middleware easy-peasy, lemon-squeezy. πŸ“


🌸 SYNOPSIS 🌸

here's the whole vibe, fr fr:

package Plack::Middleware::Foo;
use parent qw( Plack::Middleware );

sub call {
    my($self, $env) = @_;
    # Do something with $env

    # $self->app is the original app
    my $res = $self->app->($env);

    # Do something with $res
    return $res;
}

# then in app.psgi
use Plack::Builder;

my $app = sub { ... } # as usual

builder {
    enable "Plack::Middleware::Foo";
    enable "Plack::Middleware::Bar", %options;
    $app;
};

see that? <hotpink>the builder block is where the magic happens</hotpink> β€” you just enable your middleware and it's giving main character energy. πŸ’…


πŸ‘‘ DESCRIPTION πŸ‘‘

Plack::Middleware is a utility base class for writing PSGI middleware. it's literally that simple β€” all you have to do is inherit from Plack::Middleware, and then implement the callback call method (or the to_app method that would return the PSGI code reference) to do the actual work. and to reach the original (wrapped) application? that's just $self->app. easy. 🫢

⚠️ listen up, this part is important: your middleware object is created at the PSGI application compile time and is persistent during the web server life cycle (unless it's a non-persistent environment such as CGI). so you should never set or cache per-request data like $env in your middleware object. like, never ever. that's a one-way ticket to Chaos City. 😭 See also "OBJECT LIFECYCLE" in Plack::Component.

wanna know how to actually enable middleware in your .psgi application file using the DSL? go read Plack::Builder. and if you're not feeling the builder DSL β€” no worries, bestie β€” you can also use the wrap method to wrap your application with a middleware:

use Plack::Middleware::Foo;

my $app = sub { ... };
$app = Plack::Middleware::Foo->wrap($app, %options);
$app = Plack::Middleware::Bar->wrap($app, %options);

slay. that's it. that's the whole thing. ✨


πŸŽ€ RESPONSE CALLBACK πŸŽ€

so the typical middleware is written like this:

package Plack::Middleware::Something;
use parent qw(Plack::Middleware);

sub call {
    my($self, $env) = @_;
    # pre-processing $env
    my $res = $self->app->($env);
    # post-processing $res
    return $res;
}

now here's the tricky little gremlin πŸ™ˆ β€” when you're post-processing the response, it could either be an immediate 3 element array ref, or a code reference that implements the delayed (streaming) interface. two totally different shapes.

dealing with these two types of response in each piece of middleware is pointless, so you're recommended to use the response_cb wrapper function in Plack::Util when implementing a post-processing middleware. it's the move. πŸ’œ

sub call {
    my($self, $env) = @_;
    # pre-processing $env
    my $res = $self->app->($env);

    return Plack::Util::response_cb($res, sub {
        my $res = shift;
        # do something with $res;
    });
}

the callback function gets a response as an array reference, and you can update that reference to implement the post-processing. in the normal case, this arrayref will have three elements (as described by the PSGI spec), but will have only two elements when using a $writer as described below. πŸ‘€

package Plack::Middleware::Always500;
use parent qw(Plack::Middleware);
use Plack::Util;

sub call {
    my($self, $env) = @_;
    my $res  = $self->app->($env);
    return Plack::Util::response_cb($res, sub {
        my $res = shift;
        $res->[0] = 500;
        return;
    });
}

in this example, the callback gets the $res and updates its first element (status code) to 500. using response_cb makes sure that this works with the delayed response too. iconic. πŸ”₯

you're not required (and honestly not recommended either) to return a new array reference β€” they will be simply ignored. πŸ™… you're suggested to explicitly return, unless you fiddle with the content filter callback (see below).

similarly, note that you have to keep the $res reference when you swap the entire response. like, this?

Plack::Util::response_cb($res, sub {
    my $res = shift;
    $res = [ $new_status, $new_headers, $new_body ]; # THIS DOES NOT WORK
    return;
});

THIS DOES NOT WORK. πŸ’€ it doesn't work because assigning a new anonymous array to $res doesn't update the original PSGI response value. you should instead do:

Plack::Util::response_cb($res, sub {
    my $res = shift;
    @$res = ($new_status, $new_headers, $new_body); # THIS WORKS
    return;
});

THIS WORKS. 🌈 notice the @$res β€” you're updating the thing in place, not replacing the reference. that's the whole secret. iykyk.

okay so the third element of the response array ref is a body, and it could be either an arrayref or an IO::Handle-ish object. the application could also make use of the $writer object if "psgi.streaming" is in effect, and in this case, the third element will not exist ("@$res == 2"). dealing with these variants is again really painful, and response_cb can take care of that too, by allowing you to return a content filter as a code reference:

# replace all "Foo" in content body with "Bar"
Plack::Util::response_cb($res, sub {
    my $res = shift;
    return sub {
        my $chunk = shift;
        return unless defined $chunk;
        $chunk =~ s/Foo/Bar/g;
        return $chunk;
    }
});

the callback takes one argument $chunk and your callback is expected to return the updated chunk. if the given $chunk is undef, it means the stream has reached the end, so your callback should also return undef, or return the final chunk and return undef when called next time. πŸ¦‹ that's the contract, bestie β€” respect it.


πŸ’… SEE ALSO πŸ’…

  • Plack
  • Plack::Builder
  • Plack::Component

and that's the page!! this was perl v5.42.3, dated 2026-10-18, from the Plack::Middleware(3) manpage. go forth and write middleware that eats. periodt. πŸ‘‘βœ¨πŸ’–

πŸ“¦ where this page comes from