CakePHP路由中间件无法保留请求头问题咨询
CakePHP 4中路由中间件设置CORS响应头后控制器无法获取的问题
我正在为API范围的路由处理CORS逻辑:收到OPTIONS请求时直接返回响应,无需进入应用程序处理;收到GET请求时添加正确的响应头,再把更新后的响应传递给控制器或后续中间件。但现在遇到的问题是,控制器中的$this->response对象不包含之前在中间件里设置的请求头。
$routes->plugin( 'Ark', ['path' => '/ark'], function (RouteBuilder $routes) { $routes->setRouteClass(DashedRoute::class); $routes->registerMiddleware('cors', function($req, $res, $next) { $lAllowed = [ 'localhost:8081', 'localhost:8082', 'localhost:8087', '172.16.1.225', '172.16.1.225:8081', '172.16.1.225:8082', '172.16.1.225:8087', '172.16.1.224', ]; if( $sOrigin = $req->getHeader('origin') ) { $sOrigin = reset($sOrigin); $sOrigin = str_replace('https://', '', $sOrigin); $sOrigin = str_replace('http://', '', $sOrigin); } if( empty($sOrigin) ) $sOrigin = $req->host(); /** @var \Cake\Http\ServerRequest $req */ /** @var \Cake\Http\Response $res */ if( in_array($sOrigin, $lAllowed) ) { //debug( 'Allow' ); $res = $res->cors($req) ->allowOrigin($req->getHeader('origin')) //only one host should be allowed when allow credentials is true ->allowMethods(['GET', 'OPTIONS']) ->allowHeaders(['Content-Type', 'X-CSRF-Token', 'Authorization']) ->allowCredentials() ->maxAge(3600) //1h ->build(); //return immediately for CORS requests if ( strtoupper($req->getMethod()) === 'OPTIONS' ) { return $res->withStatus(200, 'You shall pass!!'); } //debug( $res ); } return $next($req, $res); }); $routes->prefix('Api', ['path' => '/api'], function(RouteBuilder $route) { // Parse specified extensions from URLs $route->setExtensions(['json']); //allow external services use this api $route->applyMiddleware('cors'); $route->prefix('V1', ['path' => '/v1'], function(RouteBuilder $route) { // Translates to `Controller\Api\V1\` namespace $lListOfResources = [ //Table names... ]; foreach ($lListOfResources as $sResourceName) { $route->resources($sResourceName, [ 'map' => [ ':id/restore' => ['action' => 'restore', 'method' => ['PUT', 'PATCH']], 'list' => ['action' => 'list', 'method' => 'GET'], 'filter/*' => ['action' => 'filter', 'method' => 'GET'], ] ]); } $route->fallbacks(); }); }); //default landing page $routes->connect('/', ['controller' => 'Pages']); } );
问题解答
- 这不是Bug,是CakePHP响应对象的设计特性导致的。CakePHP的
Response是不可变对象,每次调用with*方法(包括cors()->build())都会返回一个新的响应实例,而非修改原对象。 - 路由中间件完全可以在控制器动作前修改响应,但控制器默认的
$this->response是从请求对象的初始属性生成的,中间件传递的新响应实例不会自动同步到控制器的$this->response中。 - 不建议将CORS逻辑放到控制器的
beforeFilter里:- CORS属于HTTP层面的处理逻辑,放在中间件更符合CakePHP的架构分层设计。
- OPTIONS请求无需进入控制器处理,中间件直接返回响应能减少不必要的流程开销。
解决办法
如果需要在控制器中访问中间件设置的响应头,可采用以下两种方式:
- 在控制器中通过
$this->getRequest()->getAttribute('response')获取中间件传递的响应实例,替代默认的$this->response。 - 在中间件中将设置好的响应头存入请求属性,后续在控制器中读取该属性并同步到响应里。
另外,你的中间件代码已经正确传递了处理后的响应实例(return $next($req, $res);中的$res是经过CORS处理后的新对象),这部分逻辑没有问题。
内容的提问来源于stack exchange,提问作者Robin Reist
相关产品推荐
相关产品推荐

