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 前

但是,在上到线上环境后(即执行了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时,结果如下:
php artisan api:cache 前

符合预期,Request 是单例的。

执行php artisan api:cache后,结果如下:
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 路由实现源码,还未能找到上述现象的原因,只能斗胆先发帖看看有没有大佬研究过类似问题,也顺便做一个记录,以免以后踩同样的坑。

附言 1  ·  4年前

已上传可复现问题的项目代码到 github,见 https://github.com/Junyi-Xiao/test-request

使用 laravel/framework:v7.5.2、dingo/api:v3.0.0 完美复现问题。

《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 3

我亲测一遍,没出现问题哈 。

4年前 评论
xinhuo (楼主) 4年前

laravel 5.8 dingo 2.3 php7.3.4 :grin:

4年前 评论
xinhuo (楼主) 4年前
lyxxxh 4年前
xinhuo (楼主) 4年前

file

file

filefile

file

你看下

4年前 评论
xinhuo (楼主) 4年前
normaluser (作者) 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!