kafkiansky / symfony-middleware
symfony的PSR-15中间件。
Requires
- php: ^8.2
- nyholm/psr7: ^1.4
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
- symfony/cache: ^7.0
- symfony/config: ^7.0
- symfony/dependency-injection: ^7.0
- symfony/event-dispatcher: ^7.0
- symfony/http-foundation: ^7.0
- symfony/http-kernel: ^7.0
- symfony/psr-http-message-bridge: ^7.0
Requires (Dev)
- phpunit/phpunit: ^10.0
- vimeo/psalm: ^5.0
This package is auto-updated.
Last update: 2024-09-13 13:10:58 UTC
README
内容
安装
composer require kafkiansky/symfony-middleware
配置
确保您已在 config/bundles.php
中添加了包。
Kafkiansky\SymfonyMiddleware\SymiddlewareBundle::class => ['all' => true],
在 packages/symfony-middleware.yaml
中创建配置文件。
symiddleware: global: ##
使用
每个中间件必须实现 Psr\Http\Server\MiddlewareInterface
接口。感谢symfony自动配置,现在中间件注册表知道您的中间件。
为了让中间件开始执行,必须在控制器类和/或方法上定义它们。
use Kafkiansky\SymfonyMiddleware\Attribute\Middleware; #[Middleware([ValidatesQueryParams::class])] final class SomeController { #[Middleware([ConvertStringsToNull::class])] public function index(): void { } }
如果控制器是可调用的,中间件只需在控制器类上定义即可
use Kafkiansky\SymfonyMiddleware\Attribute\Middleware; #[Middleware([ValidatesQueryParams::class, ConvertStringsToNull::class])] final class SomeController { public function __invoke(): void { } }
分组
如果您想使用中间件列表,可以在 symfony_middleware.yaml
配置文件中定义中间件组。
symiddleware: groups: debug: if: '%env(RUN_DEBUG_MIDDLEWARE)%' middlewares: - 'App\Middleware\TrackRequestTime' - 'App\Middleware\EnableSqlLogger'
现在在控制器类或方法上定义此中间件。
use Kafkiansky\SymfonyMiddleware\Attribute\Middleware; #[Middleware(['debug'])] final class SomeController { public function __invoke(): void { } }
请注意配置文件中的 if
参数。此参数告诉中间件运行者何时可以运行中间件组。如果为假,则此中间件将不会执行。
全局
如果您想在每个请求上运行中间件列表,则需要全局中间件部分。此关键字是保留的,不支持 if
参数。
symiddleware: global: - App\Controller\SetCorsHeaders groups: web: middlewares: - 'App\Middleware\ModifyRequestMiddleware'
现在 App\Controller\SetCorsHeaders
中间件将在每个请求上执行。
示例
- 简单的修改请求的中间件
use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; final class ModifyRequestMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { return $handler->handle($request->withAttribute(__CLASS__, 'handled')) } }
- 修改响应的中间件
use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; final class ModifyResponseMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $response = $handler->handle($request) return $response->withHeader('x-developer', 'kafkiansky'); } }
- 停止执行的中间件
use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Nyholm\Psr7\Response; final class StopExecution implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $response = new Response(200, [], json_encode(['success' => false])); return $response; } }
在此示例中,控制器将不会执行。
- 使用symfony响应停止执行
use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Nyholm\Psr7\Response; use Kafkiansky\SymfonyMiddleware\Psr\PsrResponseTransformer; use Symfony\Component\HttpFoundation\JsonResponse; final class StopExecution implements MiddlewareInterface { private PsrResponseTransformer $psrResponseTransformer; public function __construct(PsrResponseTransformer $psrResponseTransformer) { $this->psrResponseTransformer = $psrResponseTransformer; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { return $this->psrResponseTransformer->toPsrResponse(new JsonResponse(['success' => false])); } }
您可以使用单个中间件、中间件列表的 Middleware
属性等方式来组合中间件组。以下所有示例都将正常工作
use Kafkiansky\SymfonyMiddleware\Attribute\Middleware; #[Middleware(['debug', 'api', SomeMiddleware::class])] #[Middleware([SomeAnotherMiddleware::class])] final class SomeController { public function __invoke(): void { } }
use Kafkiansky\SymfonyMiddleware\Attribute\Middleware; #[Middleware(['debug', 'api', SomeMiddleware::class])] final class SomeController { #[Middleware([SomeAnotherMiddleware::class, 'web'])] #[Middleware(['tracking'])] public function index(): void { } }
此外,您还可以使用嵌套组
symiddleware: global: - App\Controller\SetCorsHeaders - web groups: web: middlewares: - 'App\Middleware\ModifyRequestMiddleware' - debug debug: if: false middlewares: - 'App\Middleware\LogSqlQuery'
重复的中间件将被删除。
自定义
PSR中间件和Symfony具有不同的不兼容的请求对象。如果您的中间件将要更改请求对象,则只有 attributes
、查询参数
、头部
和 解析后的主体
将从psr请求复制到symfony请求。如果您希望更改此行为,您可以更改 Kafkiansky\SymfonyMiddleware\Psr\PsrRequestCloner
接口的绑定,以实现您的实现。
缓存
该包在生产环境中使用缓存以防止反射的使用。首先,包将搜索 app.cache_middleware
参数。如果包找不到它,它将使用 kernel.environment
定义,并在设置为 prod
时缓存属性。
即使找不到其属性,包也将缓存所有控制器。这种方法将允许记住所有控制器,而不再使用反射。
真实世界示例
假设您有一些需要通过基本认证授权访问的端点。编写中间件
# services.yaml services: _defaults: autowire: true autoconfigure: true bind: $basicUser: 'root' $basicPassword: 'secret'
// Authorization Middleware use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Nyholm\Psr7\Response; final class AuthorizeRequests implements MiddlewareInterface { private string $basicUser; private string $basicPassword; public function __construct(string $basicUser, string $basicPassword) { $this->basicUser = $basicUser; $this->basicPassword = $basicPassword; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $user = $request->getServerParams()['PHP_AUTH_USER'] ?? null; $passwd = $request->getServerParams()['PHP_AUTH_PW'] ?? null; if ($user === $this->basicUser && $passwd === $this->basicPassword) { return $handler->handle($request); } return new Response(401, [ 'WWW-Authenticate' => 'Basic realm="Backend"' ]); } }
# example configuration symiddleware: groups: basic: middlewares: - App\Middleware\AuthorizeRequests
// Some controller use Symfony\Component\HttpFoundation\JsonResponse; use Kafkiansky\SymfonyMiddleware\Attribute\Middleware; final class SomeController { #[Middleware(['basic'])] // via middleware group public function writeArticle(): JsonResponse { } #[Middleware([App\Middleware\AuthorizeRequests::class])] // via concrete class public function deleteArticle(): JsonResponse { } }
中间件
测试
$ composer test
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。