@Chiang
2020-02-18T13:53:07.000000Z
字数 4184
阅读 577
Laravel
2020-02
对于一个Web应用来说,在一个请求真正被处理之前,我们可能会对请求进行各种各样的判断,然后才可以让它继续传递到后续的处理步骤中去。如果我们用if ... else ...
这样子来处理的话,一旦需要判断的条件越来越多,会使得代码更加难以维护,系统间的耦合会增加,而中间件就是为了解决这类问题,应运而生的。
我们可以把这些判断逻辑,独立出来,开发成互不相干的中间件,然后注册到系统中去,从而实现与if ... else ...
同样的效果。如果使用过JavaWeb中的Filter,或者Spring中的AOP的同学,对Laravel中的中间件就不会感到太陌生。
由于该系列文章的定位是“扫盲式”学习,以学会用为主,并不会过多的深入源码级别进行分析。下面就细说一下到底如何使用Laravel中的中间件。
在项目根目录下执行以下语句:
php artisan make:middleware CheckRole
这个命令会在app/Http/Middleware
目录下创建一个CheckRole
类。生成的代码如下所示:
class CheckRole
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// 执行业务逻辑操作
// ......
return $next($request);
}
}
现在我们就可以在handle
函数中添加我们的处理逻辑。理解中间件的最好方式就是将中间件看做HTTP请求到达目标动作之前必须经过的“层”,每一层都会检查请求,并执行相关的业务逻辑操作。对于中间件的业务逻辑的执行顺序有以下两种:
- 处理请求之前
<?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;
}
}
定义完中间件以后,那中间件如何才能真正的应用到系统中呢?这个时候就需要说到中间件的注册,在Laravel中,我们可以将中间件注册为以下三种类型:
- 全局中间件
- 路由中间件
- 中间件组
下面就详细的说说如何注册中间件。
全局中间件指注册为全局的中间件会应用用于每一个HTTP请求。如果一个中间件在每次的HTTP请求时都要用到,那么就需要把它入到app/Http/Kernel.php
文件里中的$middleware
数组中即可。在$middleware
数组中的中间件就称为全局中间件。
路由中间件指为路由而开发的中间件。使用在路由身上的中间件是在app/Http/Kernel.php
的$routeMiddleware
数组属性中定义的。如果我们创建了一个新的给路由使用的中间件,就需要在它添加到$routeMiddleware
这个数组里,并给这个中间件一个key——相当于中间件的名字。例如:
protected $routeMiddleware = [
'admin.auth' => \App\Http\Middleware\AdminAuthenticate::class,
'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,
];
注册完路由中间件后,我们就可以将中间件应用我们定义的路由,例如这样:
Route::get('admin/profile', function () {
//
})->middleware('auth');
又比如这样:
Route::middleware(['first', 'second'])->group(function () {
Route::get('/', function () {
// Uses first & second Middleware
});
Route::get('user/profile', function () {
// Uses first & second Middleware
});
});
中间件组中包含多个中间件,但它的使用和路由中间件是完全一样的。中间件组在app/Http/Kernel.php
的$middlewareGroups
属性中定义,每个中间件组还有一个对应的key。
Laravel项目中,预设并使用了两个中间件组:web
和api
,它们分别用在了routes/web.php
和routes/api.php
上,前者是定义Web接口路由的地方,后者是定义API接口路由的地方。这两种类型的接口,各自都有一些通用的中间件,然后放在了一个组里进行管理。例如这样:
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',
],
];
使用中间件组带来一个好处——一次定义,即可使用多个中间件。
注意,
routes/web.php
文件里的中间件已经默认使用了web中间件组,这是在RouteServiceProvider
中定义的,在routes/web.php
中定义路由时,是无需为路由额外分配web中间件组的。
中间件还可以接收额外的自定义参数,例如,如果应用需要在执行给定动作之前验证认证用户是否拥有指定的角色,可以创建一个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');
有时候中间件可能需要在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是从服务容器中找到并创建一个新的中间件实例使用的,如果你只要使用同一个中间件实例,那么就要使用容器的singleton
方法注册中间件了。
参考资料:
Laravel初级教程之中间件