1.定义中间件
要创建一个新的中间件,可以通过 Artisan 命令 make:middleware:
php artisan make:middleware CheckToken
请求之前/之后的中间件
一个中间件是请求前还是请求后执行取决于中间件本身。比如,以下中间件会在请求处理前执行一些任务:
<?php namespace App\Http\Middleware; use Closure; class BeforeMiddleware{ public function handle($request, Closure $next) { // 执行动作,比如执行验证 return $next($request); } }
而下面这个中间件则会在请求处理后执行其任务:
<?php namespace App\Http\Middleware; use Closure; class AfterMiddleware{ public function handle($request, Closure $next) { $response = $next($request); // 执行动作,比如 return $response; } }
2.注册中间件
中间件分三类,分别是全局中间件、中间件组和指定路由中间件:
全局中间件
如果你想要定义的中间件在每一个 HTTP 请求时都被执行,只需要将相应的中间件类添加到 app/Http/Kernel.php 的数组属性 $middleware 中即可:
image.png
分配中间件到指定路由
如果你想要分配中间件到指定路由,首先应该在 app/Http/Kernel.php 文件中分配给该中间件一个 key,默认情况下,该类的 $routeMiddleware 属性包含了 Laravel 自带的中间件,要添加你自己的中间件,只需要将其追加到后面并为其分配一个 key,例如:
// 在 App\Http\Kernel 类中.../** * 应用的路由中间件列表 * * 这些中间件可以分配给路由组或者单个路由 * * @var array */protected $routeMiddleware = [ 'auth' => \Illuminate\Auth\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, 'token' => CheckToken::class ];
中间件在 HTTP Kernel 中被定义后,可以使用 middleware 方法将其分配到路由:
Route::get('/', function () { // })->middleware('token');
可以使用数组分配多个中间件到路由:
Route::get('/', function () { // })->middleware('token', 'auth');
中间件组
有时候你可能想要通过指定一个键名的方式将相关中间件分到同一个组里面,这样可以更方便地将其分配到路由中,这可以通过使用 HTTP Kernel 提供的 $middlewareGroups 属性实现。
Laravel 自带了开箱即用的 web 和 api 两个中间件组,分别包含可以应用到 Web 和 API 路由的通用中间件:
/** * 应用的中间件组 * * @var array */protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ 'throttle:60,1', 'bindings', ], ];
中间件组使用和分配单个中间件同样的语法被分配给路由和控制器动作。再次申明,中间件组的目的只是让一次分配给路由多个中间件的实现更加方便:
Route::get('/', function () { // })->middleware('web'); Route::group(['middleware' => ['web']], function () { // });
默认情况下, RouteServiceProvider 自动将中间件组 web 应用到 routes/web.php 文件,将中间件组 api 应用到 routes/api.php (不需要再将这些中间件分配给路由)。
当然我们可以自己设置自己的中间件组,以实现更灵活的中间件分配策略:
/** * 应用的中间件组. * * @var array */protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ 'throttle:60,1', 'bindings', ], 'blog' => [ 'token', ] ];
我们修改 routes/web.php 下面的中间件分配方式:
Route::group(['middleware'=>['blog']],function() { Route::get('/', function () { return view('welcome', ['website' => 'Laravel']); }); Route::view('/view', 'welcome', ['website' => 'Laravel学院']); });
3.中间件参数
中间件还可以接收额外的自定义参数,例如,如果应用需要在执行给定动作之前验证认证用户是否拥有指定的角色,可以创建一个 CheckRole 来接收角色名作为额外参数。
额外的中间件参数会在 $next 参数之后传入中间件:
<?php namespace App\Http\Middleware; use Closure; class CheckRole{ /** * 处理输入请求 * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string $role * @return mixed * translator http://laravelacademy.org */ public function handle($request, Closure $next, $role) { if (! $request->user()->hasRole($role)) { // Redirect... } return $next($request); } }
中间件参数可以在定义路由时通过 : 分隔中间件名和参数名来指定,多个中间件参数可以通过逗号分隔:
Route::put('post/{id}', function ($id) { // })->middleware('role:editor'); //中间件判断是否有修改权限
4.终端中间件
终端中间件,可以理解为一个善后的后台处理中间件。有时候中间件可能需要在 HTTP 响应发送到浏览器之后做一些工作,比如,Laravel 内置的 session 中间件会在响应发送到浏览器之后将 Session 数据写到存储器中,为了实现这个功能,需要定义一个终止中间件并添加 terminate 方法到这个中间件:
<?php namespace Illuminate\Session\Middleware; use Closure; class StartSession{ public function handle($request, Closure $next) { return $next($request); } public function terminate($request, $response) { // 存储session数据... } }
terminate 方法将会接收请求和响应作为参数。定义了一个终端中间件之后,还需要将其加入到 app/Http/Kernel.php 文件的全局中间件列表中。
当调用中间件上的 terminate 方法时,Laravel 将会从服务容器中取出一个该中间件的新实例,如果你想要在调用 handle 和 terminate 方法时使用同一个中间件实例,则需要使用容器提供的 singleton 方法以单例的方式将该中间件注册到容器中。