阅读时间:1 分钟
0 字

中间件机制

中间件是处理 HTTP 请求的过滤器,基于 PSR-15 标准实现,在请求到达控制器前后执行额外逻辑。

中间件概述

执行流程

HTTP请求 → 中间件1 → 中间件2 → 控制器 → 中间件2 → 中间件1 → HTTP响应

主要用途

  • 请求预处理 - 认证、授权、数据验证
  • 响应后处理 - 添加头信息、日志记录
  • 条件控制 - 根据条件决定是否继续处理

创建中间件

基本结构

php
<?php
namespace App\Web\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class AuthMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // 请求前处理
        $token = $request->getHeaderLine('Authorization');
        
        if (empty($token)) {
            throw new \Core\Exception\UnauthorizedException('Token required');
        }
        
        // 验证 Token 并添加用户信息
        $user = $this->validateToken($token);
        $request = $request->withAttribute('user', $user);
        
        // 继续处理请求
        $response = $handler->handle($request);
        
        // 响应后处理
        return $response->withHeader('X-Auth-User', $user->name);
    }

    private function validateToken(string $token): object
    {
        // Token 验证逻辑
        return (object)['id' => 1, 'name' => 'User'];
    }
}

常用中间件类型

1. 认证中间件

php
class AuthMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $token = $this->extractToken($request);
        
        if (!$token || !$this->validateToken($token)) {
            throw new \Core\Exception\UnauthorizedException();
        }
        
        return $handler->handle($request);
    }
}

2. 限流中间件

php
class RateLimitMiddleware implements MiddlewareInterface
{
    public function __construct(private int $maxRequests = 100, private int $timeWindow = 3600) {}

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $clientIp = $this->getClientIp($request);
        $key = "rate_limit:{$clientIp}";
        
        $count = \Core\App::cache()->get($key);
        if ($count === null) {
            $count = 0;
        }
        
        if ($count >= $this->maxRequests) {
            throw new \Core\Exception\TooManyRequestsException();
        }
        
        \Core\App::cache()->set($key, $count + 1, $this->timeWindow);
        
        return $handler->handle($request);
    }
}

使用中间件

在模块中注册中间件

开发者在自己模块的 App.php 中注册中间件:

php
<?php
namespace App\Web;

use Core\App\AppExtend;
use Core\Bootstrap;
use Core\Route\Route;
use App\Web\Middleware\AuthMiddleware;
use App\Web\Middleware\LogMiddleware;

class App extends AppExtend
{
    public function register(Bootstrap $bootstrap): void
    {
        // 注册 API 路由并添加认证中间件
        $apiRoute = new Route('/api', 'api', new AuthMiddleware('api'));
        \Core\App::route()->set('api', $apiRoute);
        
        // 注册管理员路由并添加多个中间件
        $adminRoute = new Route('/admin', 'admin',
            new AuthMiddleware('admin'),
            new PermissionMiddleware('admin', 'Admin'),
            new LogMiddleware()
        );
        \Core\App::route()->set('admin', $adminRoute);
    }
}

中间件组合

多个中间件按顺序执行,推荐顺序:

php
public function register(Bootstrap $bootstrap): void
{
    $apiRoute = new Route('/api', 'api',
        new AuthMiddleware('api'),           // 1. 认证
        new PermissionMiddleware('api', 'User'), // 2. 权限
        new ApiMiddleware($callback)         // 3. 签名验证
    );
    \Core\App::route()->set('api', $apiRoute);
}

全局中间件

也可以在模块中注册全局中间件:

php
public function init(Bootstrap $bootstrap): void
{
    // 注册全局中间件
    $bootstrap->web->addMiddleware(new LogMiddleware());
    $bootstrap->web->addMiddleware(new CorsMiddleware());
}

路由级别控制

跳过认证

php
#[Route(methods: 'GET', route: '/public/info', auth: false)]
public function publicInfo(): ResponseInterface
{
    return send($response, '公开信息');
}

跳过权限检查

php
#[Action(methods: 'GET', route: '/list', can: false)]
public function list(): ResponseInterface
{
    // 需要认证但不需要权限
    return send($response, 'success');
}

中间件执行顺序

中间件按注册顺序执行,遵循"洋葱模型":

php
// 注册顺序
$route = new Route('/api', 'api',
    new LogMiddleware(),     // 3. 最外层
    new AuthMiddleware(),   // 2. 中间层  
    new CorsMiddleware()    // 1. 最内层
);

// 实际执行顺序
// 请求: Log → Auth → Cors → 控制器
// 响应: 控制器 → Cors → Auth → Log

获取认证信息

php
public function profile(ServerRequestInterface $request): ResponseInterface 
{
    $auth = $request->getAttribute('auth');
    return send($response, '获取成功', [
        'user_id' => $auth['id'],
        'username' => $auth['username']
    ]);
}

最佳实践

1. 单一职责

php
// ✅ 好的做法:单一职责
class AuthMiddleware implements MiddlewareInterface
{
    // 只负责认证
}

class LogMiddleware implements MiddlewareInterface  
{
    // 只负责日志
}

// ❌ 避免:多重职责
class AuthLogMiddleware implements MiddlewareInterface
{
    // 既做认证又做日志
}

2. 无状态设计

php
// ✅ 好的做法:无状态
class RateLimitMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // 使用外部存储(Redis/Database)保存状态
        $count = \Core\App::cache()->get("rate_limit:{$ip}");
    }
}

// ❌ 避免:有状态
class BadRateLimitMiddleware implements MiddlewareInterface
{
    private array $requestCounts = []; // 状态数据
}

通过合理使用中间件,可以实现关注点分离,让代码更加模块化和可维护。