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;
}
The tricky thing about post-processing the response is that it could either be an immediate 3 element
array ref, or a code reference that implements the delayed (streaming) interface.
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.
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 the 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.
You're not required (and 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.
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, since 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;
});
The third element of the response array ref is a body, and it could be either an arrayref or
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.