为静态资源设置单独的 Laravel 路由文件,且不使用 Session 和 Cookie

Laravel

一早上醒来就看到 Slack 通知,这可不是什么好兆头

一夜之间,我的 Redis 实例完全填满了,我们的 Redis 做两件事:

  1. 会话存储
  2. 缓存一些数据位,没有什么实质内容

我用 TablePlus 看看在 Redis 能发现什么。由于 Laravel 使用随机散列作为缓存密钥的一部分,而且有效载荷是经过编码/加密的,因此很难说清楚到底发生了什么。

但是,我可以看到有2个 Redis 数据库(db0db1)。检查 config/databases.php 文件时,我发现确实为 Redis 定义了两个相应的数据库。

# File config/databases.php
return [
    // Things ommitted here...


    /*
    |--------------------------------------------------------------------------
    | Redis Databases
    |--------------------------------------------------------------------------
    |
    | Redis is an open source, fast, and advanced key-value store that also
    | provides a richer body of commands than a typical key-value system
    | such as APC or Memcached. Laravel makes it easy to dig right in.
    |
    */

    'redis' => [

        'client' => env('REDIS_CLIENT', 'phpredis'),

        'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        ],

        'default' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_DB', '0'),
        ],

        'cache' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_CACHE_DB', '1'),
        ],

    ],
];

default 连接使用 db0cache 连接使用 db1。 事实证明,会话存储在 db0 中,而我们在代码中缓存的内容存储在 db1。 默认数据库 db0 的密钥比缓存数据库多得多。

应用程序创建太多会话.

如何创建会话?

每个 web 请求(针对在 routes/web.xml 中定义的路由)都会创建一个会话(或者使用一个现有的会话)。Web 应用程序在创建会话时返回 cookie。Web 浏览器存储这些 cookie,并在发出额外的 Web 请求时将 cookie 发送回来。这使得我们的 web 应用程序可以知道哪个会话对于给定的用户是有效的。

如果浏览器没有在每个请求上返回一个 cookie,那么用户将无法继续登录。

基于 API 的会话不是这样工作的。每个会话都被创建,然后再每个请求中销毁——没有涉及到 cookie。相反,客户端需要在每个 web 请求上发送它的身份验证信息(通常是某种令牌)。

Redis 为何爆炸?

那么, 是什么导致我们的 Redis 实例在会话中爆炸呢?

动态生成的资产嵌入到其他人的网站上,我们有两个这样的例子:

  1. 我们的应用程序生成一个 .js 文件,其他人在他们的网站上嵌入这个文件。
  2. 同样的,我们的应用程序还生成了 .svg 图片。

这些路由在我们的 routes/web.php 中定义:

Route::get('/embed.js');
Route::get('/{project}/share.js');

你看出问题了吗?消费者把这些信息放到他们自己的网站上。每次有人访问他们的网站,我们的应用程序就会收到一个 HTTP 请求,用于获取文件或图片,这就创建了一个会话。

这意味着我们的客户网络流量也在我们的网络应用程序中创建会话!

如何减少会话创建

解决方案是确保我们不为特定路线创建会话。 说起来简单,但是我们如何做到这一点呢?

事实证明,Cookies和 Sessions 的创建都是在 Laravel 的中间件中完成的。这很好,因为我们可以控制应用到每个路由的中间件。

为了确保某些路由不会创建 Sessions/返回 Cookies,我喜欢创建一个具有不同中间件的单独路由文件。

要做到这一点,我们需要做一些事情:

  1. 创建一个新的 routes/static.php 文件 (名称是任意的)
  2. 将中间件添加到 app/Http/Kernel.php
  3. 更新 app/Providers/RouteServiceProvider.php 加载我们的新的路由文件,并应用新的中间件。

新的路由文件非常简单——我们创建一个新文件并将路由定义移动到里面:

# 文件 routes/static.php`

# 从 routes/web.php 复制他们
Route::get('/embed.js');
Route::get('/{project}/share.js');

然后我们可以更新 Kernel.php 文件来创建一个新的中间件。我们可以复制 web 中间件,移除处理 cookies 和会话的中间件:

# app/Http/Kernel.php 文件

.
.
.

    /**
     * 应用路由中间件组
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'static' => [
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ], 
    ];

.
.
.

我们创建了一个新的中间件组,命名为 static。它类似于 API 中间件,但是我们没有限流。

最后,我们需要注册新的路由文件,并应用我们新的 static 中间件组。 我们将通过更新 RouteServiceProvider.php 来做到这一点:

# File app/Providers/RouteServiceProvider.php

# 省略……
    /**
     * 定义路由模型绑定、模式过滤器等。
     *
     * @return void
     */
    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/web.php'));

            Route::middleware('static')
                ->namespace($this->namespace)
                ->group(base_path('routes/static.php'));
        }); 
    }

# 省略……

RouteServiceProvider 注册每个路由文件,并确定它们的中间件。 这就是 routes/web.php 中的内容如何获取分配给它的 web 中间件组。

这也是我们创建自己的路由文件的原因——我们希望避免使用 web 中间件组,并且能够在需要时向新路由文件添加路由。

结果

结果是我们的两个 “静态” 路由 (一个返回动态生成的资源——一个 JS 文件和 一个 SVG 图片 ) 不在创建 Session 和 Cookies。

这允许我们的 Redis 实例恢复。会话过期后,从 Redis 中删除了它们。由于客户流量不再在会话存储中创建会话,所以 Redis 实例再也不会填满!

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://laravel-news.com/custom-route-fi...

译文地址:https://learnku.com/laravel/t/62439

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 1

最好的方式,静态资源不经过后端

2年前 评论

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