laravel 的路由中间件
简介#
Laravel 中间件提供了一种方便的机制来过滤进入应用的HTTP请求。例如,Laravel 内置了一个中间件来验证用户的身份认证 ,
如果没有通过身份认证,中间件会将用户重定向到登陆界面,但是,如果用户被认证,中间件将允许该请求进一步进入该应用。
当然,除了身份认证以外,还可以编写另外的中间件来执行各种任务,例如:CORS中间件可以负责为所有离开应用的响应添加合适的头部信息:
日志中间件可以记录所有传入应用的请求。
Laravel 自带了一些中间件,包括身份验证,CSRF 保护等,所有这些中间件都位于
app/Http/Middleware 目录。
定义中间件#
运行Artisan 命令 make:middleware 创建新的中间件:
php artisan make:middleware CheckAge
该命令将会在app/Http/Middleware 目录内新建一个CheckAge 类。在这个中间件里,我们仅允许提供的参数 age
大于 200 的请求访问该路由。否则 我们将用户重定向到 home .
<?php
namespace App\Http\Middleware;
use Closure;
class CheckAge
{
/*
处理传入的请求
@param \Illuminate\Http\Request $request
@param \Closure $next
@return mixed
*/
public function handle($request,Closure $next)
{
if($request->age <= 200){
return redirect('home');
}
return $next($request);
}
}
?>
如你所见,若给定的 age 小于等于 200 ,那中间件将返回一个HTTP重定向到客户端;否则,请求将进一步传递到应用中,
要让请求继续传递到应用程序中(即 允许[ 通过 ] 中间件验证的) ,只需使用$request 作为参数去调用回调函数 $next.
最好将中间件想象为一系列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 Closre;
class AfterMiddleware
{
public function handle($request,Closure $next)
{
$response = $next($request);
// 执行动作
return $request;
}
}
注册中间件
全局中间件#
如果你想让中间件在你应用的每个HTTP请求期间运行,只需要在 app/Http/kernel.php 类中的$middleware 属性
里列出这个中间件类。
为路由分配中间件#
如果要为特定的路由分配中间件,首先应该在 app/Http/Kernel.php 文件内为该中间件指定一个键。默认情况下,
Kernel类的$routeMiddleware 属性包含Laravel 内置的中间件条目。要加入自定义的,只需要把它附加到列表后并为其分配一个自定义
键 即可,例如
...
// 在 App\Http\Kernel 类中
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,
];
一旦在kernel中定义了中间件,就可使用middleware方法将中间价分配给路由
Route::get('admin/profile',function(){
//
})->middleware('auth');
你还可以为路由分配多个中间件:
Route::get('/',function(){
//
})->middleware('first','second');
分配中间件时,你还可以传递完整的类型:
use App\Http\Middleware\CheckAge;
Route::get('admin/profile',function(){
//
})->middleware(CheckAge::class);
中间件组#
有时你可能想用单一的 键 为几个中间件分组。使其更容易分配到路由。可以使用Kernel类的$middlewareGroups 属性来实现。
Laravel 自带的web 和 api 中间件组包含了你可能会应用到Web UI 和 API 路由的常见的中间件:
/** * 应用程序的路由中间件组 * * @var array */ 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', 'auth:api', ], ];
可以使用与单个中间件相同的语法将中间件组分配给路由和控制器操作,重申一遍,中间件组只是更方便的实现了一次为路由分配多个中间件
Route::get('/',function(){
//
})->midlleware('web');
Route::group(['middleware'=>['web']],function(){
//
})
中间件参数#
中间件也可以接受额外的参数,例如,如果应用需要在运行特定操作前验证经过身份认证的用户是否具备给定的 角色 你可以新建一个 CleckRole
中间件,由它来接收 角色 名称作为附加参数。
附加的中间件参数应该在 $next 参数之后被传递:
<?php namespace App\Http\Middleware; use Closure; class CheckRole { /** * 处理传入的请求 * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string $role * @return mixed */ public function handle($request, Closure $next, $role) { if (! $request->user()->hasRole($role)) { // 重定向... } return $next($request); } }
定义路由时通过一个: 来隔开中间名称和参数来指定中间件参数,多个参数就使用逗号分隔:
Route::put('post/{id}',function($id)
{
//
})->middleware('role:editor');
Terminable 中间件#
有时中间件可能需要在HTTP响应发送到浏览器之后处理一些工作。比如,Laravel 内置对的 [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)
{
//Store the session data...
}
}
terminate 方法应该同时接收和响应,一旦定义了这个中间件,你应该将它添加到路由列表
或 app/Http/Kernel.php 文件的全局中间件中。
在你的中间件上调用terninate调用时,Laravel会从服务容器中解析
一个新的中间件实例,如果要在调用 handle和terminate 方法时
使用同一个中间件实例,就使用容器的singleton方法向容器注册中间件。