引言
Laravel 中间件提供了一种优雅的机制来过滤进入应用程序的 HTTP 请求。5 它就像一系列”层”,每个 HTTP 请求都必须通过这些层才能到达应用程序的核心逻辑。中间件可以执行各种任务,如身份验证、CORS 处理、日志记录等。
中间件基础概念
什么是中间件
中间件是位于 HTTP 请求和应用程序响应之间的过滤层。3 它可以:
- 检查和修改传入的请求
- 执行身份验证和授权
- 记录请求日志
- 处理 CORS 跨域请求
- 验证 CSRF 令牌
- 限制请求频率
中间件的工作原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?php
namespace App\Http\Middleware;
use Closure;
class ExampleMiddleware {
public function handle($request, Closure $next) { $response = $next($request); return $response; } }
|
创建自定义中间件
使用 Artisan 命令创建
1 2
| php artisan make:middleware CheckAge
|
实现中间件逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?php
namespace App\Http\Middleware;
use Closure;
class CheckAge {
public function handle($request, Closure $next) { if ($request->age <= 18) { return redirect('home')->with('error', '年龄不符合要求'); }
return $next($request); } }
|
注册中间件
全局中间件
全局中间件会在每个 HTTP 请求中运行。1 在 app/Http/Kernel.php 文件中注册:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel {
protected $middleware = [ \App\Http\Middleware\TrustProxies::class, \App\Http\Middleware\CheckForMaintenanceMode::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, \App\Http\Middleware\LogRequests::class, ]; }
|
路由中间件
路由中间件只在特定路由上运行。2 首先在 $routeMiddleware 数组中注册:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'check.age' => \App\Http\Middleware\CheckAge::class, 'admin' => \App\Http\Middleware\AdminMiddleware::class, ];
|
在路由中使用中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| Route::get('/profile', function () { return view('profile'); })->middleware('auth');
Route::get('/admin/dashboard', function () { return view('admin.dashboard'); })->middleware(['auth', 'admin']);
use App\Http\Middleware\CheckAge;
Route::get('/adult-content', function () { return view('adult-content'); })->middleware(CheckAge::class);
Route::group(['middleware' => ['auth', 'verified']], function () { Route::get('/dashboard', 'DashboardController@index'); Route::get('/settings', 'SettingsController@index'); });
|
中间件组
中间件组允许将多个中间件打包成一个键,便于批量应用。4
定义中间件组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ],
'api' => [ 'throttle:60,1', 'bindings', ],
'admin' => [ 'auth', 'verified', 'admin.permission', 'admin.log', ],
'api.v1' => [ 'throttle:100,1', 'auth:api', 'bindings', 'cors', ], ];
|
使用中间件组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Route::get('/', function () { return view('welcome'); })->middleware('web');
Route::group(['middleware' => ['admin']], function () { Route::get('/admin/users', 'Admin\UserController@index'); Route::get('/admin/settings', 'Admin\SettingsController@index'); });
Route::group(['prefix' => 'api/v1', 'middleware' => ['api.v1']], function () { Route::get('/users', 'Api\V1\UserController@index'); Route::post('/users', 'Api\V1\UserController@store'); });
|
Laravel 内置中间件详解
身份验证中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware {
protected function redirectTo($request) { if (! $request->expectsJson()) { return route('login'); } } }
|
CSRF 保护中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware {
protected $except = [ 'api/*', 'webhooks/*', ]; }
|
频率限制中间件
1 2 3 4 5 6 7 8 9
| Route::middleware('throttle:60,1')->group(function () { Route::get('/api/users', 'UserController@index'); });
Route::middleware('throttle:10,1')->get('/api/search', function () { return response()->json(['results' => []]); });
|
中间件参数
传递参数给中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?php
namespace App\Http\Middleware;
use Closure;
class CheckRole {
public function handle($request, Closure $next, $role) { if (! $request->user() || ! $request->user()->hasRole($role)) { abort(403, '权限不足'); }
return $next($request); } }
|
在路由中传递参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Route::get('/admin', function () { return view('admin'); })->middleware('role:admin');
Route::get('/editor', function () { return view('editor'); })->middleware('role:admin,editor');
Route::group(['middleware' => ['auth', 'role:admin']], function () { Route::resource('users', 'UserController'); });
|
高级中间件应用
Terminable 中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <?php
namespace App\Http\Middleware;
use Closure;
class LogAfterRequest {
public function handle($request, Closure $next) { return $next($request); }
public function terminate($request, $response) { \Log::info('Request completed', [ 'url' => $request->fullUrl(), 'method' => $request->method(), 'status' => $response->getStatusCode(), 'duration' => microtime(true) - LARAVEL_START, ]); } }
|
API 认证中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| <?php
namespace App\Http\Middleware;
use Closure; use Illuminate\Http\Request;
class ApiAuthenticate {
public function handle(Request $request, Closure $next) { $token = $request->bearerToken(); if (!$token) { return response()->json([ 'error' => 'Token 缺失', 'code' => 401 ], 401); }
$user = $this->validateToken($token); if (!$user) { return response()->json([ 'error' => 'Token 无效', 'code' => 401 ], 401); }
$request->setUserResolver(function () use ($user) { return $user; });
return $next($request); }
private function validateToken($token) { return \App\Models\User::where('api_token', $token)->first(); } }
|
CORS 中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?php
namespace App\Http\Middleware;
use Closure;
class Cors {
public function handle($request, Closure $next) { $response = $next($request);
$response->headers->set('Access-Control-Allow-Origin', '*'); $response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); $response->headers->set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With'); $response->headers->set('Access-Control-Max-Age', '86400');
return $response; } }
|
中间件最佳实践
1. 中间件顺序很重要
1 2 3 4 5 6 7
| Route::group([ 'middleware' => ['cors', 'auth:api', 'permission'] ], function () { });
|
2. 使用中间件组简化配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| protected $middlewareGroups = [ 'admin.api' => [ 'cors', 'auth:api', 'role:admin', 'throttle:100,1', ], 'user.web' => [ 'web', 'auth', 'verified', ], ];
|
3. 中间件性能优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <?php
namespace App\Http\Middleware;
use Closure; use Illuminate\Support\Facades\Cache;
class CacheMiddleware {
public function handle($request, Closure $next, $minutes = 60) { $key = 'response:' . md5($request->fullUrl()); if (Cache::has($key)) { return Cache::get($key); } $response = $next($request); if ($request->isMethod('GET') && $response->getStatusCode() === 200) { Cache::put($key, $response, now()->addMinutes($minutes)); } return $response; } }
|
4. 错误处理和日志记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <?php
namespace App\Http\Middleware;
use Closure; use Illuminate\Support\Facades\Log;
class ErrorHandlingMiddleware {
public function handle($request, Closure $next) { try { return $next($request); } catch (\Exception $e) { Log::error('中间件捕获异常', [ 'exception' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'url' => $request->fullUrl(), 'method' => $request->method(), 'user_id' => $request->user() ? $request->user()->id : null, ]); if ($request->expectsJson()) { return response()->json([ 'error' => '服务器内部错误', 'code' => 500 ], 500); } return redirect()->back()->with('error', '操作失败,请稍后重试'); } } }
|
实际应用场景
1. API 版本控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <?php
namespace App\Http\Middleware;
use Closure;
class ApiVersionMiddleware {
public function handle($request, Closure $next, $version = 'v1') { $requestVersion = $request->header('API-Version', 'v1'); if ($requestVersion !== $version) { return response()->json([ 'error' => "不支持的 API 版本: {$requestVersion}", 'supported_version' => $version ], 400); } config(['app.api_version' => $version]); return $next($request); } }
|
2. 请求日志记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <?php
namespace App\Http\Middleware;
use Closure; use Illuminate\Support\Facades\Log;
class RequestLoggingMiddleware {
public function handle($request, Closure $next) { $startTime = microtime(true); Log::info('请求开始', [ 'url' => $request->fullUrl(), 'method' => $request->method(), 'ip' => $request->ip(), 'user_agent' => $request->userAgent(), 'user_id' => $request->user() ? $request->user()->id : null, ]); $response = $next($request); $duration = microtime(true) - $startTime; Log::info('请求完成', [ 'url' => $request->fullUrl(), 'method' => $request->method(), 'status' => $response->getStatusCode(), 'duration' => round($duration * 1000, 2) . 'ms', ]); return $response; } }
|
总结
Laravel 中间件是一个强大而灵活的功能,它提供了一种优雅的方式来处理 HTTP 请求的过滤和预处理。通过合理使用全局中间件、路由中间件和中间件组,我们可以:
- 提高代码复用性:将通用逻辑抽取到中间件中
- 增强安全性:实现身份验证、授权和 CSRF 保护
- 改善性能:通过缓存和频率限制优化应用性能
- 简化维护:集中管理横切关注点
- 提升用户体验:处理 CORS、错误处理等
在实际开发中,建议根据项目需求合理设计中间件架构,注意中间件的执行顺序,并充分利用 Laravel 提供的内置中间件来快速构建安全、高效的 Web 应用程序。