Sometimes you may run into cases where you would like at least 1 middleware to pass from multiple middlewares.
Laravel cannot do this out of the box, so I created a middleware that proxies other middleware and passes as soon as one passes.
Sometimes you may run into cases where you would like at least 1 middleware to pass from multiple middlewares.
Laravel cannot do this out of the box, so I created a middleware that proxies other middleware and passes as soon as one passes.
<?php | |
// This route requires either clientCredentials or auth (with the api guard) middleware to pass, rather than both | |
Route::get('/', 'ExampleController@index')->middleware('any:clientCredentials,auth:api'); |
<?php | |
/** | |
* The application's route middleware. | |
* | |
* These middleware may be assigned to groups or used individually. | |
* | |
* @var array | |
*/ | |
protected $routeMiddleware = [ | |
// Some auth middleware | |
'auth' => \Illuminate\Auth\Middleware\Authenticate::class, | |
'clientCredentials' => \App\Http\Middleware\ClientCredentials::class, | |
'any' => \App\Http\Middleware\Any::class, | |
]; |
<?php | |
namespace App\Http\Middleware; | |
use Closure; | |
use Exception; | |
class Any | |
{ | |
private $hasPassed; | |
private $caughtException; | |
public function __construct() | |
{ | |
$this->hasPassed = false; | |
} | |
/** | |
* Handle an incoming request. | |
* | |
* @param \Illuminate\Http\Request $request | |
* @param \Closure $next | |
* @param array $parameters An array of middleware to apply | |
* @return mixed | |
*/ | |
public function handle($request, Closure $next, ...$parameters) | |
{ | |
// Loop through each middleware | |
collect($parameters)->each(function ($parameter) use ($request) { | |
// As soon as one middleware passes, don't bother checking the rest. One is all we need | |
if ($this->hasPassed) { | |
return false; | |
} | |
// Grab any parameters that have been passed into the given middleware | |
// e.g. for "auth:api", $middlewareParams would be ["api"] | |
$middlewareParts = explode(':', $parameter); | |
$middlewareParams = array_slice($middlewareParts, 1); | |
// Get an instance of the middleware class | |
$middleware = $this->getMiddleware($middlewareParts[0]); | |
try { | |
// See if the middleware passes | |
$middleware->handle($request, function () { | |
$this->hasPassed = true; | |
}, ...$middlewareParams); | |
} catch (Exception $e) { | |
$this->caughtException = $e; | |
} | |
}); | |
// Forward the request on, if at least one of the middleware passed | |
if ($this->hasPassed) { | |
return $next($request); | |
} | |
// If any exceptions were thrown, re-throw the last one | |
if ($this->caughtException) { | |
throw $this->caughtException; | |
} | |
} | |
/** | |
* Get an instance off the middleware class | |
* @param The name of the middleware $name | |
* @return mixed | |
*/ | |
private function getMiddleware($name) | |
{ | |
$allMiddleware = app('router')->getMiddleware(); | |
return app()->make(array_get($allMiddleware, $name)); | |
} | |
} |