dingo/API 使用路由缓存前后获取 Request 实例表现不一致
背景
版本信息:laravel/framework:v5.8.37,dingo/api:v2.4.7
使用 dingo/api
定义一个路由(routes/api.php
):
$router = app('api.router');
$router->version('v1', [
'namespace' => 'App\Http\Controllers\Api',
'middleware' => \App\Http\Middleware\TestMiddleware::class,
], function (\Dingo\Api\Routing\Router $router) {
$router->get('test/request', 'TestController@testRequest');
});
其中,中间件 TestMiddleware 如下:
class TestMiddleware
{
public function handle($request, Closure $next)
{
$request->merge([
'_from' => 'middle',
]);
return $next($request);
}
}
控制器 TestController 如下:
namespace App\Http\Controllers\Api;
//use Dingo\Api\Http\Request;
use Illuminate\Http\Request;
class TestController extends ApiController
{
/**
* @var Request
*/
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
//$this->request = request();
}
public function testRequest()
{
dd($this->request->get('_from'));
}
}
问题
正常来说,Request 为单例模式,中间件对 Request 实例的修改能反馈到控制器中,在本地开发环境下(也就是无执行php artisan api:cache
的环境),接口响应符合预期:
但是,在上到线上环境后(即执行了php artisan api:cache
的环境),接口响应却改变了:
TestController 的构造函数中,
$this->request = $request;
和$this->request = request();
都能复现一样的现象,下文也是。
TestController 引用 Request 时用
use Illuminate\Http\Request;
或use Dingo\Api\Http\Request;
都能复现一样的现象,下文也是。
调试
将 TestController 控制器方法改为:
public function testRequest(Request $request)
{
dd([
'$this->request === request()' => $this->request === request(),
'$this->request === $request' => $this->request === $request,
'$this->request->get(\'_from\')' => $this->request->get('_from'),
'$request === request()' => $request === request(),
'$request->get(\'_from\')' => $request->get('_from'),
]);
}
无执行php artisan api:cache
时,结果如下:
符合预期,Request 是单例的。
执行php artisan api:cache
后,结果如下:
可以看出,「在控制器 TestController 的构造函数中实例化的 Request」与「中间件(TestMiddleware)、控制器方法(testRequest)中的 Request 实例」不属于同一实例,即不满足单例模式。
使用 Laravel 原生路由,在 routes/web.php
中定义(此时 TestController 引用use Illuminate\Http\Request;
):
Route::middleware(\App\Http\Middleware\TestMiddleware::class)
->get('test/request', 'Api\TestController@testRequest');
可以轻易测出在php artisan api:cache
执行前后,Request 都是严格单例的,符合预期。
此时就能看到一个有趣的现象:php artisan api:cache
执行后,同样的控制器方法(TestController@testRequest),定义在不同路由中,展现出了不同的结果。
总结
本人不才,还没摸透 Laravel 原生路由和 dingo/api 路由实现源码,还未能找到上述现象的原因,只能斗胆先发帖看看有没有大佬研究过类似问题,也顺便做一个记录,以免以后踩同样的坑。
已上传可复现问题的项目代码到 github,见 https://github.com/Junyi-Xiao/test-request
使用 laravel/framework:v7.5.2、dingo/api:v3.0.0 完美复现问题。
我亲测一遍,没出现问题哈 。
laravel 5.8 dingo 2.3 php7.3.4 :grin:
你看下