Laravel中可以把HTTP中间件看做“装饰器”,在请求到达最终动作之前对请求进行过滤和处理。
中间件在Laravel中有着广泛的应用,比如用户认证、日志、维护模式、开启Session、从Session中获取错误信息,以及上一篇教程中提到的CSRF验证,等等。
中间件类默认存放在app/Http/Middleware
目录下。
我们在《HTTP路由实例教程(二)—— 路由命名和路由分组》一文已经演示了如何创建中间件以及中间件的基本使用。
自定义中间件类只需要定义一个handle
方法即可,然后我们将主要业务逻辑定义在该方法中,如果我们想在请求处理前执行业务逻辑,则在$next
闭包执行前执行业务逻辑操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php namespace App\Http\Middleware; use Closure; class BeforeMiddleware { public function handle($request, Closure $next) { // 执行业务逻辑操作 return $next($request); } } |
如果想要在请求处理后执行中间件业务逻辑,则在$next
闭包执行后执行操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php namespace App\Http\Middleware; use Closure; class AfterMiddleware { public function handle($request, Closure $next) { $response = $next($request); // 执行动作 return $response; } } |
我们处理的大部分操作都是第一种场景,即在请求处理前执行操作,比如用户认证、CSRF验证、维护模式等都是这样,但也有用到第二种场景的时候,比如StartSession
中间件,该中间件在请求处理前后都有操作,其handle
方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public function handle($request, Closure $next) { $this->sessionHandled = true; //如果session驱动已配置,那么我们需要开启session以便为应用准备好数据 //注意Laravel session并没有使用原生的PHP session相关方法,因为它们显得那样蹩脚 if ($this->sessionConfigured()) { $session = $this->startSession($request); $request->setSession($session); } $response = $next($request); // 同样,如果session经过配置那么我们需要关闭session以便将session数据持久化到某些存储介质中 // 我们还会添加session id到响应头cookie中 if ($this->sessionConfigured()) { $this->storeCurrentUrl($request, $session); $this->collectGarbage($session); $this->addCookieToResponse($response, $session); } return $response; } |
此外,定义好中间件后,需要在app/Http/Kernel.php
文件中注册该中间件,如果我们定义的中间件想要在全局有效,即每次请求都会调用,则将该中间件追加到$middleware
属性数组;否则如果中间件只是在某些特定的路由中使用,则将其追加到$routeMiddleware
属性数组,并在路由定义时使用middleware
选项指定。关于这一点我们已经在路由分组中有所陈述,这里不再赘述。
除了请求实例$request
和闭包$next
之外,中间件还可以接收额外参数,我们还是以TestMiddleware
为例,现在要求年龄在18岁以上的男性才能访问指定页面,handle
方法定义如下:
1 2 3 4 5 6 7 8 |
public function handle($request, Closure $next, $gender) { if($request->input('age')>=18 && $gender==$request->input('gender')){ return $next($request); }else{ return redirect()->route('refuse'); } } |
对应的路由配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
Route::group(['middleware'=>'test:male'],function(){ Route::get('/write/laravelacademy',function(){ //使用Test中间件 }); Route::get('/update/laravelacademy',function(){ //使用Test中间件 }); }); Route::get('/age/refuse',['as'=>'refuse',function(){ return "18岁以上男子才能访问!"; }]); |
可终止的中间件是指定义了terminate
方法的中间件,terminate
方法会在一次请求生命周期的末尾执行一些操作。比如StartSession
中间件定义了该方法,在响应数据发送到浏览器之后将session数据保存起来。
可终止的中间件需要追加到app/Http/Kernel.php
类的全局中间件列表即$middleware
属性数组中。
调用中间件的terminate
方法时,Laravel会从服务容器中取出新的中间件实例,所以如果想要调用handle
方法和terminate
方法时使用的是同一个中间件实例,需要使用singleton
方法将该中间件注册到服务容器。