Immediately respond from Middleware

Hi there,

Is there a way for a middleware to immediately return a response to the client without going through the routes?

I’m thinking of using middleware to check for IP and if its not in the range of IPs I accept, i want to return a response saying “i don’t know you” or the like.

I’m currently using the withAttribute and then checking the attribute i set there, but i want to add the middleware to a group of routes and adding a check in each route is really just troublesome.

Any thoughts? how can i just reject/respond immediately/skip routes from the middleware itself?

I think this is what you are looking for, simple if statement in middleware? (using slim v4)

//....
class ContentLengthMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        if (SOMECONDITION)  {
            // usual flow, go deeper in Middlewares stack
            return $handler->handle($request); // will return usual response
        }

        $response = new Response(); // your completely custom Response, ignoring usual middleware stack
        // do your stuff

        return $response;
    }
}

Thanks for the reply, but your answer is talking about skipping processing INSIDE the middleware, then continue on to the routes (get/post/etc) right?

I want a middleware to check something and if it meets the condition, to skip the routes. For example:

$checkIpMiddleware = function(...){...};
$app->get('/lala', function(...){...})->add($checkIpMiddleware);

I want the code inside the GET /lala to completely be ignored if the IP does not match any of my accepted ones in $checkIpMiddleware and immediately return a response to the client.

I’ve been able to do that by doing withAttribute in the middleware and then getAttribute in the route, like:

$ipAllowed = $request->getAttribute('ip_allowed');
if (!isset($ipAllowed)) {
    return $response;
}

but if i have a group, for example:

$app->group('/admin', function(...){
    $group->('/login', function (Request $request, Response $response, $args) {
        $ipAllowed = $request->getAttribute('ip_allowed');
        if (!isset($ipAllowed)) {
            return $response;
        }
         // code
    });
    $group->('/client', ...{
       //same check
          $ipAllowed = $request->getAttribute('ip_allowed');
          if (!isset($ipAllowed)) {
               return $response;
          }
    });
    $group->(....
    // more
})

That would mean copy pasting alot of the ip_allowed condition check into the start of all of those right?

Is there something less troublesome to write?

jDolba response is correct.

Returning a Response in the middleware ends the middleware chain, stoping whatever middleware you have next (dependending on the stage of the middleware it can end the chain before routing, for example).

What make the chain running one middleware after another is calling return $handler->handle($request);

Thanks for the reply, is it because the routing part is also considered a middleware?
Sorry i’m new to this.

So that means i can do this:

$checkCrmIPMiddleware = function (Request $request, RequestHandler $handler) {
    $serverParams = $request->getServerParams();

    $ipList = [
        '999.999.999.999'
    ];

    if (in_array($serverParams['REMOTE_ADDR'], $ipList)) {
        $request = $request->withAttribute('ip_allowed', 'y');
        $response = $handler->handle($request);
        return $response;
    } else {
        $response = new Response();
        $response->getBody()->write('Nope');
        return $response;
    }
};

then using it in:

$app->group('/admin', function (RouteCollectorProxy $group) {
    
    $group->post('/user', function (Request $request, Response $response, $args) {
        ...
        
        return $response;
    });
})->add($checkCrmIPMiddleware );

I should get a ‘Nope’ response if my IP is not in the list right?

But i’m getting this instead:

Cannot instantiate interface Psr\Http\Message\ResponseInterface

Thanks again for the help!

Yes, that is correct.

That error seems related to the ‘nope’ Response you are creating. I’m assuming here you are trying to create a new response from the response interface, which would be incorrect.

For example, when using Slim-Psr7 you can do something like this:

$responseFactory = new \Slim\Psr7\ResponseFactory;

$response = $responseFactory->createResponse();

Hope it helps :slight_smile:

1 Like

This is, because a middleware must implement the PSR-15 interface.

@odan

I think what you said would give him a Type error instead. Instantiation errors happens when “new” is called. Maybe “new” is calling the Response Interface instead of a Response Object… ?!

The PSR-15 signature of the function seems correct.

I would not say that this a valid PSR-15 interface, because it’s a closure (function) and not a class.

$checkCrmIPMiddleware = function (Request $request, RequestHandler $handler) {

This worked:

...
use Slim\Psr7\Factory\ResponseFactory;
...
if (in_array($serverParams['REMOTE_ADDR'], $ipList)) {
    $request = $request->withAttribute('ip_allowed', 'y');
    $response = $handler->handle($request);
    return $response;
} else {
    $responseFactory = new ResponseFactory();
    $response = $responseFactory->createResponse();
    $response->getBody()->write('No');
    return $response;
}

Thanks for all the help!

Changed to this:

//put this in the middleware closure declaration so i can use $app inside it
...
use ($app) 
...
//then use that to get the factory to create the response
$response = $app->getResponseFactory()->createResponse();

However, I got the above from the custom error handlers and render part and actually scarpped that and now changed my code to throw errors instead with a custom exception so i can pass the status code as well:

class ExceptionWithStatus extends \Exception {
    private $statusCode;

    public function __construct($message, $statusCode) {
        $this->statusCode = $statusCode;
        parent::__construct($message);
    }

    public function getStatusCode() {
        return $this->statusCode;
    }
}

$checkCrmIPMiddleware = function (Request $request, RequestHandler $handler) {
    $serverParams = $request->getServerParams();

    $ipList = [
        'xxx.xx.xxx.xxx'
    ];

    if (in_array($serverParams['REMOTE_ADDR'], $ipList)) {
        $response = $handler->handle($request);
        return $response;
    } else {
        throw new ExceptionWithStatus('Nope', 401);
    }
};

the errors will be caught by the custom error response closure (from the docs) and i can append my custom status code:

... //error response closure
   return $response->withStatus($exception->getStatusCode());
}
1 Like