如何为Aura Router实现路由专属中间件?当前写法报错
解决Aura Router路由专属中间件报错“Invalid request handler: array”的问题
嘿,我明白你碰到的困扰了——想给特定路由加专属中间件,结果用数组把中间件和控制器方法绑在一起时,触发了Invalid request handler: array错误。这是因为Aura Router和Request Handler的默认逻辑里,路由的handler参数只接受可调用字符串(比如控制器方法路径)、闭包,或是能被容器解析的服务标识,并不直接支持数组形式的「中间件+handler」组合。
下面给你两种可行的解决方案,都能实现路由级专属中间件的效果:
方案1:利用路由选项存储中间件,动态注入处理链
这种方法的核心是把路由专属中间件存在路由的options里,在Dispatcher流程中先匹配路由,再根据路由信息动态把中间件加入处理链。
步骤1:定义路由时添加中间件选项
$routerContainer = new RouterContainer(); $map = $routerContainer->getMap(); // 给目标路由绑定专属中间件到options $map->get('index', '/', 'App\Http\Controllers\HomeController::index') ->setOptions([ 'middleware' => [new SampleRouteMiddleware()] ]); // 其他路由正常定义即可 $map->get('about', '/about', 'App\Http\Controllers\AboutController::index');
步骤2:创建路由中间件处理类
这个类会在路由匹配完成后,检查当前路由是否有专属中间件,有则把它们插入到请求处理链中:
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Zend\Stratigility\Middleware\RequestHandlerStack; class RouteMiddlewareHandler implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { // 从请求属性中获取Aura Router匹配到的路由对象 $route = $request->getAttribute('route'); if ($route && isset($route->options['middleware'])) { // 把路由专属中间件和原handler组成新的处理栈 $middlewareStack = $route->options['middleware']; array_push($middlewareStack, $handler); $newHandler = new RequestHandlerStack($middlewareStack); return $newHandler->handle($request); } // 无路由中间件则直接执行原流程 return $handler->handle($request); } }
步骤3:调整Dispatcher的中间件顺序
关键:必须把路由匹配中间件放在路由中间件处理器之前,这样才能先拿到路由信息:
$request = ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); $requestContainer = new RequestHandlerContainer(); $dispatcher = new Dispatcher([ new SampleAppMiddleware(), // 全局应用中间件(所有路由生效) new AuraRouter($routerContainer), // 先完成路由匹配 new RouteMiddlewareHandler(), // 处理当前路由的专属中间件 new RequestHandler($requestContainer), // 最终执行控制器方法 ]); $response = $dispatcher->dispatch($request);
方案2:通过RequestHandler容器注册带中间件包裹的处理程序
这种方法是提前把「中间件+控制器方法」的组合注册到容器里,路由直接指向这个注册好的服务标识。
步骤1:在容器中注册带中间件的处理服务
$requestContainer = new RequestHandlerContainer(); // 注册被SampleRouteMiddleware包裹的首页控制器处理程序 $requestContainer->set('home.index', function () { $controller = new App\Http\Controllers\HomeController(); // 把中间件和控制器方法组成处理栈 return new RequestHandlerStack([ new SampleRouteMiddleware(), fn(ServerRequestInterface $req) => $controller->index($req) ]); });
步骤2:路由指向容器中的服务标识
$routerContainer = new RouterContainer(); $map = $routerContainer->getMap(); // 路由的handler直接使用容器中注册的服务名 $map->get('index', '/', 'home.index');
步骤3:正常配置Dispatcher
$request = ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); $dispatcher = new Dispatcher([ new SampleAppMiddleware(), new AuraRouter($routerContainer), new RequestHandler($requestContainer), ]); $response = $dispatcher->dispatch($request);
两种方案各有优势:方案1适合给多个路由批量配置中间件的场景,方案2则更灵活,适合对单个路由做精细的处理链定制。
内容的提问来源于stack exchange,提问作者bmatovu
相关产品推荐
相关产品推荐

