Zend-mvc middleware

I was hopeful to create a system where developers can pick and choose their middleware stack components using the zend-mvc middleware config.

For example, this middleware’s process method:

class JsonWrappingMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, DelegateInterface $delegate)
    {
        /** @var JsonResponse $data */
        $jsonDecodeNextResponse =  json_decode(
            (string) $delegate->process($request)->getBody(),
            true
        );
        $current        = ['c' => 'd'];
        return new JsonResponse(array_merge($jsonDecodeNextResponse, $current));
    }
}

Works well if the next middleware in the stack looks like this:

public function process(ServerRequestInterface $request, DelegateInterface $delegate)
{
    return new JsonResponse(['a' => 'b']);
}

However, for this exercise, they’d have to detect that there is indeed a ‘next’ element in the stack. The DelegateInterface only publishes process.

How can I detect that it’s the ‘end of the road’? I’d like to do something like:

public function process(ServerRequestInterface $request, DelegateInterface $delegate)
{
    $current = ['c' => 'd'];

    /** @var JsonResponse $data */
    if ($delegate) {
        $current = array_merge(json_decode(
            (string)$delegate->process($request)->getBody(),
            true
        ), $current);
    }

    return new JsonResponse($current);
}

That way I can sandwich a bunch of these, or, simply use one.

Based on what you’ve detailed here, and information you posted to me in Slack, it looks like you’re conditionally appending response data.

As you note, DelegateInterface only publishes the method process(). This method is guaranteed to return a response. How that response is returned is unknown. The Delegate may be a queue or stack of middleware (Stratigility, and by extension, Expressive, use a queue), or it may just be a class that returns a canned response. In Expressive v2, we have a concept of a “default delegate”, which is invoked if the middleware pipeline created in and executed by the Application instance exhausts itself. Our default delegate returns a “Not Found” response, as, generally speaking, if you reach that point, routing most likely failed.

My point is, you cannot know if the delegate implements a stack or queue, and, if it does, if it has more middleware to execute.

However, if you’re appending response data, you have a few options.

First, you could create an alternate default delegate, and register it as the Zend\Expressive\Delegate\DefaultDelegate service, and have it return a canned response for you to control. Your code could essentially skip execution if that response is discovered. (In point of fact, you can likely do that now, by looking at how the response is created by the NotFoundDelegate.)

Second, make sure you operate on the response returned, and investigate it. For instance:

$response = $delegate->process($request);
if (! $response instanceof JsonResponse) {
    // Cannot handle this
    return $response;
}

$responseData = json_decode((string) $response->getBody(), true);
return new JsonResponse(array_merge($responseData, $dataToInject));

Finally, if you need to, you could potentially even look for a marker within the response: a special header, a special response type, a discrete element, etc. If these are present, or absent, your middleware then knows whether or not to operate.

Essentially, think of these as post-processors, operating on what is returned from the delegate, and deciding whether or not they can or cannot operate on the response returned. Do that by looking at the response; assume the delegate returns a response, and you’ll be fine.

1 Like