laniakea/middleware-priority

更灵活地管理Laravel的HTTP中间件优先级。

v1.0.0 2024-03-21 09:53 UTC

This package is auto-updated.

Last update: 2024-08-29 20:55:30 UTC


README

Latest Version on Packagist Tests Total Downloads

此包为Laravel 11+应用提供了更灵活地管理HTTP中间件优先级的方法。

请注意,此包仅与全新的Laravel 11+应用兼容(或已升级到Laravel 11+且具有新目录结构和/或应用引导的应用)。

您可以将中间件简单地追加/前置到优先级列表,或者进行更复杂的事情,例如:

  • 将中间件添加到特定中间件之前或之后;
  • 交换两个中间件的位置;
  • 从优先级列表中删除中间件。

此包提供了默认的Laravel优先级列表(最新更新:Laravel 11.0.7;请查看DefaultMiddlewarePriority类),因此您可以用它作为自定义优先级列表的基础。

安装

您可以通过composer安装此包

composer require laniakea/middleware-priority

用法

此包旨在与Laravel的Application::configure()构建器(位于bootstrap/app.php文件中)一起使用。

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        // Manage priority list here.
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

创建优先级管理器

withMiddleware回调内部创建Laniakea\MiddlewarePriority\MiddlewarePriorityManager的新实例,以开始使用中间件优先级管理器。请注意,默认情况下优先级列表将为空(除非您在创建管理器实例之前修改了它)。

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Laniakea\MiddlewarePriority\MiddlewarePriorityManager;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $manager = new MiddlewarePriorityManager($middleware);
        
        // Manage priority list here.
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

使用Laravel的默认优先级

如果您想使用Laravel的默认优先级列表,可以使用静态方法MiddlewarePriorityManager::withDefaults创建具有默认优先级列表的管理器实例。

withDefaults()方法的第二个参数接受用户定义的默认中间件优先级列表(如果为null或未传递,则使用默认的Laravel优先级列表)。

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Laniakea\MiddlewarePriority\MiddlewarePriorityManager;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $manager = MiddlewarePriorityManager::withDefaults($middleware); // create manager with default Laravel's priority list
        // $manager = MiddlewarePriorityManager::withDefaults($middleware, ['App\\SomeMiddleware']); // create manager with custom priority list
        
        // Manage priority list here.
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

追加中间件

以下假设在withMiddleware回调内部创建了并使用了管理器。

使用append(string|array $middleware)方法将中间件追加到优先级列表。

<?php

use App\Http\Middleware\FirstMiddleware;
use App\Http\Middleware\SecondMiddleware;
use App\Http\Middleware\ThirdMiddleware;

$manager = MiddlewarePriorityManager::withDefaults($middleware);
$manager->append(FirstMiddleware::class);
// priority list: [..., FirstMiddleware::class]

$manager->append([SecondMiddleware::class, ThirdMiddleware::class]);
// priority list: [..., FirstMiddleware::class, SecondMiddleware::class, ThirdMiddleware::class]

前置中间件

使用prepend(string|array $middleware)方法将中间件前置到优先级列表。

<?php

use App\Http\Middleware\FirstMiddleware;
use App\Http\Middleware\SecondMiddleware;
use App\Http\Middleware\ThirdMiddleware;

$manager = MiddlewarePriorityManager::withDefaults($middleware);
$manager->prepend(FirstMiddleware::class);
// priority list: [FirstMiddleware::class, ...]

$manager->prepend([SecondMiddleware::class, ThirdMiddleware::class]);
// priority list: [SecondMiddleware::class, ThirdMiddleware::class, FirstMiddleware::class, ...]

在特定中间件之前添加中间件

使用before(string $middleware, string|array $newMiddleware)方法在特定中间件之前添加中间件。

<?php

use App\Http\Middleware\FirstMiddleware;
use Illuminate\Routing\Middleware\SubstituteBindings;

$manager = MiddlewarePriorityManager::withDefaults($middleware);
$manager->before(SubstituteBindings::class, FirstMiddleware::class);
// priority list: [..., FirstMiddleware::class, SubstituteBindings::class, ...]

您也可以将中间件数组作为第二个参数使用。在这种情况下,新中间件将按照数组的顺序添加。

<?php

use App\Http\Middleware\FirstMiddleware;
use App\Http\Middleware\SecondMiddleware;
use Illuminate\Routing\Middleware\SubstituteBindings;

$manager = MiddlewarePriorityManager::withDefaults($middleware);
$manager->before(SubstituteBindings::class, [FirstMiddleware::class, SecondMiddleware::class]);
// priority list: [..., FirstMiddleware::class, SecondMiddleware::class, SubstituteBindings::class, ...]

在特定中间件之后添加中间件

使用after(string $middleware, string|array $newMiddleware)方法在特定中间件之后添加中间件。

<?php

use App\Http\Middleware\FirstMiddleware;
use Illuminate\Routing\Middleware\SubstituteBindings;

$manager = MiddlewarePriorityManager::withDefaults($middleware);
$manager->after(SubstituteBindings::class, FirstMiddleware::class);
// priority list: [..., SubstituteBindings::class, FirstMiddleware::class, ...]

您也可以将中间件数组作为第二个参数使用。在这种情况下,新中间件将按照数组的顺序添加。

<?php

use App\Http\Middleware\FirstMiddleware;
use App\Http\Middleware\SecondMiddleware;
use Illuminate\Routing\Middleware\SubstituteBindings;

$manager = MiddlewarePriorityManager::withDefaults($middleware);
$manager->after(SubstituteBindings::class, [FirstMiddleware::class, SecondMiddleware::class]);
// priority list: [..., SubstituteBindings::class, FirstMiddleware::class, SecondMiddleware::class, ...]

交换中间件位置

使用swap(string $what, string $with)方法交换两个中间件的位置。

<?php

use App\Http\Middleware\FirstMiddleware;
use App\Http\Middleware\SecondMiddleware;

$manager = MiddlewarePriorityManager::withDefaults($middleware, [FirstMiddleware::class, SecondMiddleware::class]);
// priority list: [FirstMiddleware::class, SecondMiddleware::class]

$manager->swap(FirstMiddleware::class, SecondMiddleware::class);
// priority list: [SecondMiddleware::class, FirstMiddleware::class]

删除中间件

使用remove(string|array $what)方法从优先级列表中删除中间件。

<?php

use App\Http\Middleware\FirstMiddleware;
use App\Http\Middleware\SecondMiddleware;

$manager = MiddlewarePriorityManager::withDefaults($middleware, [FirstMiddleware::class, SecondMiddleware::class]);
// priority list: [FirstMiddleware::class, SecondMiddleware::class]

$manager->remove(FirstMiddleware::class);
// priority list: [SecondMiddleware::class]

您也可以使用中间件数组。所有列出的中间件都将从优先级列表中删除。

<?php

use App\Http\Middleware\FirstMiddleware;
use App\Http\Middleware\SecondMiddleware;
use App\Http\Middleware\ThirdMiddleware;
use Illuminate\Routing\Middleware\SubstituteBindings;

$manager = MiddlewarePriorityManager::withDefaults($middleware);
$manager->remove([SecondMiddleware::class, ThirdMiddleware::class]);
// priority list: [FirstMiddleware::class]

在从优先级列表中删除中间件后,其位置将由分组中间件中的注册顺序决定。请参见以下示例。

<?php

use App\Http\Middleware\FirstMiddleware;
use App\Http\Middleware\SecondMiddleware;
use App\Http\Middleware\ThirdMiddleware;
use App\Http\Middleware\FourthMiddleware;
use App\Http\Middleware\FifthMiddleware;

$middleware->appendToGroup('web', [
    FirstMiddleware::class,
    SecondMiddleware::class,
    ThirdMiddleware::class,
    FourthMiddleware::class,
    FifthMiddleware::class,
]);

$manager = MiddlewarePriorityManager::withDefaults($middleware);
$manager->prepend(FirstMiddleware::class);
// priority list: [FirstMiddleware::class, ...]

$manager->prepend(SecondMiddleware::class);
// priority list: [SecondMiddleware::class, FirstMiddleware::class, ...]

$manager->before(FirstMiddleware::class, FourthMiddleware::class);
// priority list: [SecondMiddleware::class, FourthMiddleware::class, FirstMiddleware::class, ...]

$manager->after(FourthMiddleware::class, ThirdMiddleware::class);
// priority list: [SecondMiddleware::class, FourthMiddleware::class, ThirdMiddleware::class, FirstMiddleware::class, ...]

$manager->remove(FourthMiddleware::class);
// priority list: [SecondMiddleware::class, ThirdMiddleware::class, FirstMiddleware::class, ...]

/*
 * Middleware will be called in following order:
 * 
 * 1. FourthMiddleware::class (was removed from the priority list);
 * 2. FifthMiddleware::class (was not added to the priority list in first place);
 * 3. SecondMiddleware::class (by priority list order);
 * 4. ThirdMiddleware::class (by priority list order);
 * 5. FirstMiddleware::class (by priority list order).
 */

获取当前优先级列表

如果您需要检索当前优先级列表,可以使用getPriority()方法。

<?php

$manager = MiddlewarePriorityManager::withDefaults($middleware);

// Manage priority list here.

$currentPriority = $manager->getPriority();

完整示例

<?php

use App\Http\Middleware\FirstMiddleware;
use App\Http\Middleware\SecondMiddleware;
use App\Http\Middleware\ThirdMiddleware;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Laniakea\MiddlewarePriority\MiddlewarePriorityManager;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        // First you need to register middleware to some group.
        $middleware->appendToGroup('web', [
            FirstMiddleware::class,
            SecondMiddleware::class,
            ThirdMiddleware::class,
        ]);
        
        // Now you can manage priority list.

        $manager = MiddlewarePriorityManager::withDefaults($middleware);
        $manager->prepend(FirstMiddleware::class);
        $manager->before(\Illuminate\Routing\Middleware\SubstituteBindings::class, SecondMiddleware::class);
        $manager->after(\Illuminate\Auth\Middleware\Authorize::class, ThirdMiddleware::class);
        
        if (thirdMiddlewareNotRequired()) {
            $manager->remove(ThirdMiddleware::class);
        } elseif (thirdMiddlewareShouldBeFirst()) {
            $manager->remove(ThirdMiddleware::class)
                ->prepend(ThirdMiddleware::class);
        } elseif (thirdAndFirstMiddlewareShoudBeSwapped()) {
            $manager->swap(FirstMiddleware::class, ThirdMiddleware::class);
        }
        
        \Log::debug('[app.php@withMiddleware] Priority list generated.', ['priority' => $manager->getPriority()]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

测试

composer test

变更日志

请参见变更日志以获取有关最近更改的更多信息。

致谢

许可证

MIT许可证(MIT)。请参见许可证文件以获取更多信息。