中间件
介绍
中间件为过滤进入应用程序的HTTP请求提供了一种方便的机制。例如,Laravel包含一个中间件,用于验证应用程序的用户是否已认证。如果用户未认证,中间件将重定向用户到登录页面。然而,如果用户已认证,中间件将允许请求进一步进入应用程序。
当然,除了认证之外,还可以编写其他中间件来执行各种任务。CORS中间件可能负责为离开应用程序的所有响应添加适当的头信息。日志中间件可能会记录所有进入应用程序的请求。
Laravel框架中包含了几种中间件,包括用于认证和CSRF保护的中间件。所有这些中间件都位于app/Http/Middleware
目录中。
定义中间件
要创建新的中间件,请使用make:middleware
Artisan命令:
php artisan make:middleware CheckAge
此命令将在app/Http/Middleware
目录中放置一个新的CheckAge
类。在此中间件中,我们将仅允许访问路由,如果提供的age
大于200。否则,我们将重定向用户回到home
URI:
<?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重定向给客户端;否则,请求将被进一步传递到应用程序中。要将请求更深入地传递到应用程序中(允许中间件“通过”),请使用$next
回调和$request
。
最好将中间件设想为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;
}
}
注册中间件
全局中间件
如果您希望中间件在应用程序的每个HTTP请求期间运行,请在app/Http/Kernel.php
类的$middleware
属性中列出中间件类。
为路由分配中间件
如果您希望将中间件分配给特定路由,您应该首先在app/Http/Kernel.php
文件中为中间件分配一个键。默认情况下,此类的$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,
];
一旦在HTTP内核中定义了中间件,您可以使用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);
中间件组
有时您可能希望将多个中间件分组在一个键下,以便更容易地分配给路由。您可以使用HTTP内核的$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 () {
//
})->middleware('web');
Route::group(['middleware' => ['web']], function () {
//
});
开箱即用,web
中间件组会自动应用于您的routes/web.php
文件,由RouteServiceProvider
提供。
中间件参数
中间件还可以接收额外的参数。例如,如果您的应用程序需要在执行给定操作之前验证认证用户是否具有给定的“角色”,您可以创建一个CheckRole
中间件,该中间件接收角色名称作为额外参数。
额外的中间件参数将在$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');
可终止中间件
有时中间件可能需要在HTTP响应准备好之后做一些工作。例如,Laravel附带的“会话”中间件在响应完全准备好之后将会话数据写入存储。如果您在中间件上定义了一个terminate
方法,它将在响应准备好发送到浏览器后自动调用。
<?php
namespace Illuminate\Session\Middleware;
use Closure;
class StartSession
{
public function handle($request, Closure $next)
{
return $next($request);
}
public function terminate($request, $response)
{
// 存储会话数据...
}
}
terminate
方法应接收请求和响应。一旦定义了可终止中间件,您应该将其添加到app/Http/Kernel.php
文件中的路由或全局中间件列表中。
在调用中间件的terminate
方法时,Laravel将从服务容器解析一个新的中间件实例。如果您希望在调用handle
和terminate
方法时使用相同的中间件实例,请使用容器的singleton
方法注册中间件。