HTTP 中间件
介绍
HTTP 中间件为过滤进入应用程序的 HTTP 请求提供了一种方便的机制。例如,Laravel 包含一个中间件,用于验证应用程序的用户是否已认证。如果用户未认证,中间件会将用户重定向到登录界面。然而,如果用户已认证,中间件将允许请求进一步进入应用程序。
当然,除了认证之外,还可以编写其他中间件来执行各种任务。例如,CORS 中间件可能负责为所有离开应用程序的响应添加适当的头信息。日志中间件可能会记录所有进入应用程序的请求。
Laravel 框架中包含了几种中间件,包括用于维护、认证、CSRF 保护等的中间件。所有这些中间件都位于 app/Http/Middleware
目录中。
定义中间件
要创建新的中间件,请使用 make:middleware
Artisan 命令:
php artisan make:middleware OldMiddleware
此命令将在 app/Http/Middleware
目录中放置一个新的 OldMiddleware
类。在这个中间件中,我们将仅允许访问路由,如果提供的 age
大于 200。否则,我们将用户重定向回 "home" URI。
<?php
namespace App\Http\Middleware;
use Closure;
class OldMiddleware
{
/**
* 运行请求过滤器。
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->input('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 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' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
];
一旦在 HTTP 内核中定义了中间件,您可以在路由选项数组中使用 middleware
键:
Route::get('admin/profile', ['middleware' => 'auth', function () {
//
}]);
使用数组将多个中间件分配给路由:
Route::get('/', ['middleware' => ['first', 'second'], function () {
//
}]);
您还可以将 middleware
方法链接到路由定义,而不是使用数组:
Route::get('/', function () {
//
})->middleware(['first', 'second']);
中间件参数
中间件还可以接收额外的自定义参数。例如,如果您的应用程序需要在执行给定操作之前验证认证用户是否具有给定的 "角色",您可以创建一个 RoleMiddleware
,该中间件接收角色名称作为额外参数。
额外的中间件参数将在 $next
参数之后传递给中间件:
<?php
namespace App\Http\Middleware;
use Closure;
class RoleMiddleware
{
/**
* 运行请求过滤器。
*
* @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}', ['middleware' => 'role:editor', function ($id) {
//
}]);
可终止中间件
有时,中间件可能需要在 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)
{
// 存储会话数据...
}
}
terminate
方法应接收请求和响应。一旦定义了可终止中间件,您应将其添加到 HTTP 内核中的全局中间件列表中。
在调用中间件的 terminate
方法时,Laravel 将从服务容器中解析中间件的新实例。如果希望在调用 handle
和 terminate
方法时使用相同的中间件实例,请使用容器的 singleton
方法注册中间件。