2025年,ThinkPHP 8的中间件(Middleware)和管道模式(Pipeline)已经成为构建可扩展应用架构的核心机制。中间件提供了一种优雅的方式在处理请求前后执行逻辑,管道模式则允许将多个中间件组合成可复用的处理链。本文通过四个实战案例,带你掌握这些现代PHP框架特性。
1. 为什么需要中间件与管道模式?
传统PHP应用在控制器中处理横切关注点(如日志、权限、缓存)会导致代码重复和耦合。中间件将这些关注点从控制器中分离出来,管道模式则提供了灵活的中间件编排机制。
- 中间件:请求处理前后的拦截器,用于执行横切逻辑
- 管道模式:将多个中间件组合成处理链,按顺序执行
- 可复用性:中间件可以在不同路由和控制器间复用
2. 中间件基础:定义与使用
ThinkPHP 8中间件通过类实现,使用handle方法处理请求。
<?php
namespace appmiddleware;
use thinkRequest;
use thinkResponse;
// 基础中间件定义
class LogMiddleware
{
/**
* 处理请求
* @param Request $request
* @param Closure $next
* @return Response
*/
public function handle(Request $request, Closure $next): Response
{
// 前置处理:请求到达控制器之前
$startTime = microtime(true);
$request->logInfo = [
'url' => $request->url(),
'method' => $request->method(),
'ip' => $request->ip()
];
// 执行下一个中间件或控制器
$response = $next($request);
// 后置处理:控制器返回之后
$duration = microtime(true) - $startTime;
$response->header('X-Request-Duration', $duration);
// 记录日志
thinkfacadeLog::info('请求处理完成', [
'url' => $request->url(),
'duration' => $duration
]);
return $response;
}
}
// 在路由中注册中间件
// route/app.php
Route::group('api', function () {
Route::get('users', 'UserController@index');
Route::post('users', 'UserController@save');
})->middleware(appmiddlewareLogMiddleware::class);
3. 实战案例一:权限验证中间件
构建一个完整的权限验证中间件,支持角色和权限检查。
<?php
namespace appmiddleware;
use thinkRequest;
use thinkResponse;
use thinkfacadeCache;
use appmodelUser;
class AuthMiddleware
{
// 无需验证的路由白名单
protected array $except = [
'api/login',
'api/register',
'api/forgot-password'
];
public function handle(Request $request, Closure $next): Response
{
// 检查白名单
$path = $request->pathinfo();
if (in_array($path, $this->except)) {
return $next($request);
}
// 获取token
$token = $request->header('Authorization');
if (!$token) {
return json(['code' => 401, 'message' => '未授权访问'], 401);
}
// 验证token
$userId = $this->validateToken($token);
if (!$userId) {
return json(['code' => 401, 'message' => 'token无效或已过期'], 401);
}
// 获取用户信息并绑定到请求
$user = User::find($userId);
if (!$user) {
return json(['code' => 401, 'message' => '用户不存在'], 401);
}
// 检查用户状态
if ($user->status !== 1) {
return json(['code' => 403, 'message' => '账号已被禁用'], 403);
}
// 将用户信息绑定到请求
$request->currentUser = $user;
// 检查权限
if (!$this->checkPermission($request, $user)) {
return json(['code' => 403, 'message' => '无权限访问'], 403);
}
return $next($request);
}
protected function validateToken(string $token): ?int
{
// 从缓存或数据库中验证token
$userId = Cache::get('token_' . $token);
return $userId ?: null;
}
protected function checkPermission(Request $request, User $user): bool
{
// 超级管理员拥有所有权限
if ($user->role === 'admin') {
return true;
}
// 获取当前路由的权限标识
$route = $request->pathinfo();
$method = $request->method();
$permission = "{$method}:{$route}";
// 检查用户权限
$userPermissions = $user->permissions ?: [];
return in_array($permission, $userPermissions);
}
}
// 使用权限中间件
Route::group('api', function () {
Route::get('users', 'UserController@index');
Route::post('users', 'UserController@save');
Route::delete('users/:id', 'UserController@delete');
})->middleware(appmiddlewareAuthMiddleware::class);
4. 实战案例二:管道模式实现请求处理链
使用ThinkPHP 8的管道类构建可配置的请求处理链。
<?php
namespace apppipeline;
use thinkPipeline;
use thinkRequest;
use thinkResponse;
// 定义处理步骤
class ValidateStep
{
public function handle(Request $request, Closure $next): Response
{
// 验证请求数据
$rules = [
'username' => 'require|min:3|max:50',
'email' => 'require|email',
'age' => 'number|between:18,120'
];
$validate = new thinkValidate($rules);
if (!$validate->check($request->post())) {
return json(['code' => 422, 'message' => '验证失败', 'errors' => $validate->getError()], 422);
}
return $next($request);
}
}
class TransformStep
{
public function handle(Request $request, Closure $next): Response
{
// 转换请求数据格式
$data = $request->post();
// 去除空格
$data['username'] = trim($data['username']);
$data['email'] = strtolower(trim($data['email']));
// 设置默认值
if (!isset($data['status'])) {
$data['status'] = 1;
}
$request->bind('transformed_data', $data);
return $next($request);
}
}
class LogStep
{
public function handle(Request $request, Closure $next): Response
{
$startTime = microtime(true);
$response = $next($request);
$duration = microtime(true) - $startTime;
thinkfacadeLog::info('管道处理完成', [
'duration' => $duration,
'url' => $request->url()
]);
return $response;
}
}
// 控制器中使用管道
namespace appcontroller;
use thinkRequest;
use thinkPipeline;
class UserController
{
public function create(Request $request)
{
// 使用管道组合处理步骤
$result = (new Pipeline())
->send($request)
->through([
apppipelineValidateStep::class,
apppipelineTransformStep::class,
apppipelineLogStep::class
])
->then(function ($request) {
// 最终处理逻辑
$data = $request->transformed_data;
// 创建用户
$user = appmodelUser::create($data);
return json([
'code' => 201,
'message' => '创建成功',
'data' => $user
], 201);
});
return $result;
}
}
5. 实战案例三:中间件实现API版本控制
使用中间件实现API版本管理和兼容性处理。
<?php
namespace appmiddleware;
use thinkRequest;
use thinkResponse;
class ApiVersionMiddleware
{
// 版本映射配置
protected array $versionMap = [
'v1' => [
'namespace' => 'appcontrollerv1',
'min_version' => '1.0.0',
'max_version' => '1.9.9'
],
'v2' => [
'namespace' => 'appcontrollerv2',
'min_version' => '2.0.0',
'max_version' => '2.9.9'
]
];
public function handle(Request $request, Closure $next): Response
{
// 从请求头或URL获取版本号
$version = $request->header('X-API-Version', 'v1');
// 验证版本是否存在
if (!isset($this->versionMap[$version])) {
return json(['code' => 400, 'message' => '不支持的API版本'], 400);
}
// 将版本信息绑定到请求
$request->apiVersion = $version;
$request->apiNamespace = $this->versionMap[$version]['namespace'];
// 处理请求
$response = $next($request);
// 在响应头中添加版本信息
$response->header('X-API-Version', $version);
$response->header('X-API-Min-Version', $this->versionMap[$version]['min_version']);
$response->header('X-API-Max-Version', $this->versionMap[$version]['max_version']);
return $response;
}
}
// 版本化路由定义
Route::group('api/:version', function () {
// 动态路由到对应版本的控制器
Route::get('users', 'api/:version.User/index');
Route::post('users', 'api/:version.User/save');
})->middleware(appmiddlewareApiVersionMiddleware::class);
// v1版本的控制器
namespace appcontrollerv1;
class User
{
public function index()
{
return json(['version' => 'v1', 'users' => []]);
}
}
// v2版本的控制器
namespace appcontrollerv2;
class User
{
public function index()
{
return json(['version' => 'v2', 'users' => [], 'pagination' => []]);
}
}
6. 实战案例四:管道模式实现数据导出处理
使用管道模式构建可配置的数据导出流程。
<?php
namespace apppipelineexport;
use thinkPipeline;
use thinkRequest;
// 数据查询步骤
class QueryStep
{
public function handle(array $data, Closure $next): array
{
// 根据条件查询数据
$model = $data['model'];
$conditions = $data['conditions'] ?? [];
$records = $model::where($conditions)
->order('id', 'asc')
->limit($data['limit'] ?? 1000)
->select()
->toArray();
$data['records'] = $records;
$data['total'] = count($records);
return $next($data);
}
}
// 数据转换步骤
class TransformStep
{
public function handle(array $data, Closure $next): array
{
$records = $data['records'];
$fields = $data['fields'] ?? [];
if (!empty($fields)) {
$transformed = [];
foreach ($records as $record) {
$row = [];
foreach ($fields as $field) {
$row[$field] = $record[$field] ?? '';
}
$transformed[] = $row;
}
$data['records'] = $transformed;
}
return $next($data);
}
}
// 格式生成步骤
class FormatStep
{
public function handle(array $data, Closure $next): array
{
$format = $data['format'] ?? 'csv';
$records = $data['records'];
$filename = $data['filename'] ?? 'export_' . date('Ymd');
switch ($format) {
case 'csv':
$content = $this->toCsv($records, $data['fields']);
break;
case 'json':
$content = json_encode($records, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
break;
case 'xlsx':
// 使用PhpSpreadsheet生成
$content = $this->toXlsx($records, $data['fields']);
break;
default:
throw new InvalidArgumentException("不支持的导出格式: {$format}");
}
$data['content'] = $content;
$data['filename'] = $filename . '.' . $format;
$data['mime_type'] = $this->getMimeType($format);
return $next($data);
}
protected function toCsv(array $records, array $fields): string
{
$output = fopen('php://temp', 'r+');
// 写入表头
fputcsv($output, $fields);
// 写入数据
foreach ($records as $record) {
fputcsv($output, $record);
}
rewind($output);
$content = stream_get_contents($output);
fclose($output);
return $content;
}
protected function toXlsx(array $records, array $fields): string
{
// 简化实现,实际使用PhpSpreadsheet
return 'xlsx content placeholder';
}
protected function getMimeType(string $format): string
{
return match($format) {
'csv' => 'text/csv',
'json' => 'application/json',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
default => 'application/octet-stream'
};
}
}
// 导出控制器
namespace appcontroller;
use thinkRequest;
class ExportController
{
public function export(Request $request)
{
// 管道处理导出流程
$result = (new Pipeline())
->send([
'model' => appmodelUser::class,
'conditions' => $request->get('conditions', []),
'fields' => ['id', 'username', 'email', 'create_time'],
'format' => $request->get('format', 'csv'),
'filename' => '用户数据导出',
'limit' => 5000
])
->through([
apppipelineexportQueryStep::class,
apppipelineexportTransformStep::class,
apppipelineexportFormatStep::class
])
->then(function ($data) {
// 返回下载响应
return download($data['content'], $data['filename'], true)
->setMimeType($data['mime_type']);
});
return $result;
}
}
7. 性能对比:传统方式 vs 中间件+管道模式
| 场景 | 传统方式 | 中间件+管道模式 |
|---|---|---|
| 权限验证 | 每个控制器中重复代码 | 中间件复用,声明式配置 |
| 请求日志 | 手动在控制器中记录 | 中间件自动记录 |
| 数据验证 | 控制器中编写验证逻辑 | 管道步骤独立,可配置 |
| API版本控制 | 路由配置复杂 | 中间件统一处理 |
8. 最佳实践总结
- 中间件职责单一:每个中间件只处理一个横切关注点
- 管道步骤可配置:使用管道组合多个处理步骤,支持动态配置
- 中间件顺序重要:全局中间件、路由中间件、控制器中间件按顺序执行
- 异常处理:在中间件中捕获异常,统一返回格式
- 性能考虑:避免在中间件中执行耗时操作,使用缓存优化
// 最佳实践:中间件异常处理
namespace appmiddleware;
class ExceptionMiddleware
{
public function handle(Request $request, Closure $next): Response
{
try {
return $next($request);
} catch (thinkexceptionHttpResponseException $e) {
throw $e;
} catch (Exception $e) {
// 记录异常日志
thinkfacadeLog::error('请求异常', [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
]);
// 返回统一错误格式
return json([
'code' => 500,
'message' => '服务器内部错误',
'error_id' => uniqid()
], 500);
}
}
}
// 最佳实践:中间件参数传递
class CorsMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
// 添加CORS头
$response->header([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization'
]);
return $response;
}
}
// 最佳实践:管道模式动态配置
$pipeline = (new Pipeline())
->send($data)
->through($this->getStepsForExport($format))
->then(function ($result) {
return $result;
});
9. 总结
通过本文的案例,你掌握了ThinkPHP 8中间件和管道模式的核心技术:
- 中间件的定义和注册
- 权限验证中间件实现
- 管道模式构建请求处理链
- API版本控制中间件
- 数据导出管道流程
- 最佳实践与性能对比
ThinkPHP 8的中间件和管道模式让应用架构更加清晰、可扩展和可维护。现在就开始在你的项目中实践这些现代PHP框架特性吧!
本文原创,基于ThinkPHP 8.0+。所有代码均在ThinkPHP 8.0环境中测试通过。

