MiddlewareInterface vs RequestHandlerInterface ? "Action" vs "Handler"?

I am a bit confused on the difference between the \Psr\Http\Server\MiddlewareInterface and \Psr\Http\Server\RequestHandlerInterface. And especially why RequestHandlerInterface exists at all ?

From what I understand MiddlewareInterface can either produce a response, or delegate to further. On the other RequestHandlerInterface can only produce a response and is not really meant to delegate any further.

So if MiddlewareInterface can already do everything that RequestHandlerInterface can, then why do we need RequestHandlerInterface ? Why not only use MiddlewareInterface, even for “terminal” middleware such as view rendering ?

Also I noticed that in Mezzio, MiddlewareInterface implementations are (or were?) sometimes referred to as “action”. So in my projects I typically only have *Action classes and no “handler” at all. Eg something along the lines of:

class ExcelAction implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $stream = createStreamForExcelFile();

        $response = new Response($stream, 200, [
            'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        ]);

        return $response;
    }
}

I know that this code will work. But would it be best-practice to instead implement it as “ExcelHandler” instead ? Why or why not ?

According to https://github.com/zendframework/zend-expressive-skeleton/pull/214#discussion_r166741401. It seems that I picked up “Action” terminology a while ago, and I should now drop it entirely. And also:

The point is: use MiddlewareInterface if what you’re creating will call on the $handler at any point; use RequestHandlerInterface if it never will.

Is this still the best-practice today ?

The middleware MiddlewareInterface sits between the incoming request and the final request handler RequestHandlerInterface. So basically what you say is correct: The MiddlewareInterface delegates to another MiddlewareInterface and the RequestHandlerInterface returns a reponse only.

You don’t need the RequestHandlerInterface but why injecting things that you don’t need. After all, it should not delegate anymore and only return a response. That’s exactly what that interface says.

Actions were a thing in the first mezzio versions. Action was coming from actions in controllers. After PSR-15 was introduced and the RequestHandlerInterface the name Action didn’t fit anymore and we named it Handler.

What you have there is that you inject things in a method which shouldn’t be there. You are returning a reponse and do not delegate the request any further. Best practice would be to use the RequestHandlerInterface. If you want to name it ExcelHandler or ExcelAction, that is completely up to you.

2 Likes

There was a bunch of discussion around this when we developed PSR-15 — essentially, whether we needed two interfaces or not.

The general consensus was that request handlers represent both the application entrypoint, and the inner-most handler to which the request is matched, while middleware represents any layer between those. In fact, a number of folks involved with the specification had no use for middleware, and simply wanted some sort of interface detailing transforming a request into a response, which was the origin of the split in interfaces.

For those of us who use middleware, though, the question is, why not just use middleware everywhere? There’s a few reasons:

  • At the application layer (the thing you’re invoking in your PHP script that creates the request from the environment), you want to typehint against something that can handle the request and return a response; there is no other argument necessary.

  • At some level, you’ll have code that can handle the request by itself, and which will never need to delegate to other middleware; at that point, why should it accept more middleware as an argument?

This latter in turn gives us something we can ultimately map to authoritatively when routing: when I match this criteria, I will route to this handler. If there just so happens to be middleware in-between, that will get executed. A request handler helps us indicate the terminal condition, the one that will guarantee a response even if other layers do not. It’s an important distinction.

The PSR-15 meta document details most of the rationale used when deciding what the interfaces would look like, and I strongly recommend reading that document to understand how they are intended to work together.

As @xtreamwayz notes, the whole “action” vs “handler” stems from pre-ratification of PSR-15, and while I understand some of the arguments for keeping the terminology around (those familiar with MVC will better understand the purpose of an action than a handler), I also feel having two names for the same thing ultimately makes things more confusing, and I would like to see us drop the “action” terminology altogether.

2 Likes

Thanks to both of you. I now see that my current code is even more confused that I thought (naming a middleware “Action”…) :crazy_face:

But thanks to your clarifications I can gradually drop the term “Action”, in favor of “Handler”. And I’ll try to stick to RequestHandlerInterface as it is what I actually need in most cases.

1 Like