cakephp 路由中间件不保留标头

zaqlnxep  于 2023-10-20  发布在  PHP
关注(0)|答案(2)|浏览(127)

我尝试为API作用域路由处理CORS。如果我收到一个OPTION请求,我会立即返回响应,这样应用程序就不必处理它。但是如果我收到一个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还是RoutingMiddleware不能在任何控制器操作之前修改响应?我应该在控制器中添加CORS逻辑beforeFilter吗?
cakephp 4.2.12 php 8.0.1

unftdfkk

unftdfkk1#

这是CakePHP 4.0的预期行为。作为参数传递给中间件的响应对象将不再传递给控制器,因为它在3.x中,中间件语法只存在部分向后兼容性原因。
在4.x中,控制器将默认使用一个新的响应对象,而中间件将从$next()调用中接收它,在较新的CakePHP版本中分别是$handler->handle()调用,其中不再存在响应参数。
基本上,中间件并不意味着能够修改控制器理论上可以接收的响应对象,它们意味着返回自定义响应对象以提前退出,或者可能修改从处理程序接收的响应对象,这可以是从控制器层返回的响应,或者从另一个中间件返回的响应。
如果你想从中间件向控制器层传递任何类型的数据,那么你应该在请求对象中传递它。
另见

*Cookbook >迁移指南> 4.0迁移指南> Http
*Cookbook >迁移指南> 4.3迁移指南>中间件
*Cookbook >中间件

dgsult0t

dgsult0t2#

感谢用户ndm i可以通过在控制器执行后修改响应来修复问题

$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' );

                //return immediately for CORS requests
                if ( strtoupper($req->getMethod()) === 'OPTIONS' )
                {
                    $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 $res->withStatus(200, 'You shall pass!!');
                }

                //run other middleware or controller action, which will generate the final response
                $res = $next($req, $res);

                //apply CORS headers
                return $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();
            }

            //run default handling, no CORS applied
            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 = [
                    /* TABLES*/
                ];
                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']);

        $routes->fallbacks();
    }
);

相关问题