这是用户在 2024-6-4 18:11 为 https://laravel.com/docs/11.x/middleware 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?
Skip to content

Middleware 中间件

Introduction 介绍

Middleware provide a convenient mechanism for inspecting and filtering HTTP requests entering your application. For example, Laravel includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to your application's login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application.
中间件提供了一个方便的机制,用于检查和过滤进入应用程序的 HTTP 请求。例如,Laravel 包括一个中间件,用于验证应用程序的用户是否已经通过身份验证。如果用户未经过身份验证,中间件将重定向用户到应用程序的登录界面。但是,如果用户已经通过身份验证,中间件将允许请求进一步进入应用程序。

Additional middleware can be written to perform a variety of tasks besides authentication. For example, a logging middleware might log all incoming requests to your application. A variety of middleware are included in Laravel, including middleware for authentication and CSRF protection; however, all user-defined middleware are typically located in your application's app/Http/Middleware directory.
可以编写额外的中间件来执行除身份验证之外的各种任务。例如,日志中间件可以记录所有发送到应用程序的请求。Laravel 包含各种中间件,包括用于身份验证和 CSRF 保护的中间件;但是,所有用户定义的中间件通常位于应用程序的 app/Http/Middleware 目录中。

Defining Middleware 定义中间件

To create a new middleware, use the make:middleware Artisan command:
要创建一个新的中间件,请使用 make:middleware Artisan 命令:

php artisan make:middleware EnsureTokenIsValid

This command will place a new EnsureTokenIsValid class within your app/Http/Middleware directory. In this middleware, we will only allow access to the route if the supplied token input matches a specified value. Otherwise, we will redirect the users back to the home URI:
此命令将在您的 app/Http/Middleware 目录中放置一个新的 EnsureTokenIsValid 类。在这个中间件中,我们只允许访问路由,如果提供的 token 输入与指定值匹配。否则,我们将将用户重定向回 home URI。

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class EnsureTokenIsValid
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->input('token') !== 'my-secret-token') {
return redirect('home');
}
 
return $next($request);
}
}

As you can see, if the given token does not match our secret token, the middleware will return an HTTP redirect to the client; otherwise, the request will be passed further into the application. To pass the request deeper into the application (allowing the middleware to "pass"), you should call the $next callback with the $request.
正如您所看到的,如果给定的 token 与我们的秘密令牌不匹配,中间件将向客户端返回 HTTP 重定向;否则,请求将进一步传递到应用程序中。要将请求传递到应用程序更深层次(允许中间件“传递”),您应该使用 $request 调用 $next 回调。

It's best to envision middleware as a series of "layers" HTTP requests must pass through before they hit your application. Each layer can examine the request and even reject it entirely.
最好将中间件设想为一系列 HTTP 请求必须通过的“层”,然后才能到达您的应用程序。每个层都可以检查请求,甚至可以完全拒绝它。

All middleware are resolved via the service container, so you may type-hint any dependencies you need within a middleware's constructor.
所有中间件都通过服务容器解析,因此您可以在中间件的构造函数中对您需要的任何依赖项进行类型提示。

Middleware and Responses
中间件和响应

Of course, a middleware can perform tasks before or after passing the request deeper into the application. For example, the following middleware would perform some task before the request is handled by the application:
当然,中间件可以在将请求传递到应用程序更深层之前或之后执行任务。例如,以下中间件将在应用程序处理请求之前执行某些任务:

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class BeforeMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// Perform action
 
return $next($request);
}
}

However, this middleware would perform its task after the request is handled by the application:
然而,此中间件将在应用程序处理请求后执行其任务:

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class AfterMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
 
// Perform action
 
return $response;
}
}

Registering Middleware 注册中间件

Global Middleware 全球中间件

If you want a middleware to run during every HTTP request to your application, you may append it to the global middleware stack in your application's bootstrap/app.php file:
如果您希望一个中间件在每个 HTTP 请求期间运行到您的应用程序中,您可以将其附加到全局中间件堆栈中,即在您的应用程序的 bootstrap/app.php 文件中:

use App\Http\Middleware\EnsureTokenIsValid;
 
->withMiddleware(function (Middleware $middleware) {
$middleware->append(EnsureTokenIsValid::class);
})

The $middleware object provided to the withMiddleware closure is an instance of Illuminate\Foundation\Configuration\Middleware and is responsible for managing the middleware assigned to your application's routes. The append method adds the middleware to the end of the list of global middleware. If you would like to add a middleware to the beginning of the list, you should use the prepend method.
提供给 withMiddleware 闭包的 $middleware 对象是 Illuminate\Foundation\Configuration\Middleware 的一个实例,负责管理分配给应用程序路由的中间件。 append 方法将中间件添加到全局中间件列表的末尾。如果您想要将中间件添加到列表的开头,应该使用 prepend 方法。

Manually Managing Laravel's Default Global Middleware
手动管理 Laravel 的默认全局中间件

If you would like to manage Laravel's global middleware stack manually, you may provide Laravel's default stack of global middleware to the use method. Then, you may adjust the default middleware stack as necessary:
如果您想要手动管理 Laravel 的全局中间件堆栈,您可以将 Laravel 的全局中间件默认堆栈提供给 use 方法。然后,您可以根据需要调整默认中间件堆栈:

->withMiddleware(function (Middleware $middleware) {
$middleware->use([
// \Illuminate\Http\Middleware\TrustHosts::class,
\Illuminate\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Http\Middleware\ValidatePostSize::class,
\Illuminate\Foundation\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
]);
})

Assigning Middleware to Routes
将中间件分配给路由

If you would like to assign middleware to specific routes, you may invoke the middleware method when defining the route:
如果您想要将中间件分配给特定路由,您可以在定义路由时调用 middleware 方法:

use App\Http\Middleware\EnsureTokenIsValid;
 
Route::get('/profile', function () {
// ...
})->middleware(EnsureTokenIsValid::class);

You may assign multiple middleware to the route by passing an array of middleware names to the middleware method:
您可以通过将中间件名称数组传递给 middleware 方法来为路由分配多个中间件:

Route::get('/', function () {
// ...
})->middleware([First::class, Second::class]);

Excluding Middleware 排除中间件

When assigning middleware to a group of routes, you may occasionally need to prevent the middleware from being applied to an individual route within the group. You may accomplish this using the withoutMiddleware method:
将中间件分配给一组路由时,您可能偶尔需要防止中间件应用于组内的单个路由。您可以使用 withoutMiddleware 方法来实现这一点:

use App\Http\Middleware\EnsureTokenIsValid;
 
Route::middleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/', function () {
// ...
});
 
Route::get('/profile', function () {
// ...
})->withoutMiddleware([EnsureTokenIsValid::class]);
});

You may also exclude a given set of middleware from an entire group of route definitions:
您还可以从整个路由定义组中排除给定的中间件集合:

use App\Http\Middleware\EnsureTokenIsValid;
 
Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/profile', function () {
// ...
});
});

The withoutMiddleware method can only remove route middleware and does not apply to global middleware.
withoutMiddleware 方法只能移除路由中间件,不适用于全局中间件。

Middleware Groups 中间件组

Sometimes you may want to group several middleware under a single key to make them easier to assign to routes. You may accomplish this using the appendToGroup method within your application's bootstrap/app.php file:
有时候,您可能希望将几个中间件分组到一个单一的键下,以便更容易地分配给路由。您可以在应用程序的 bootstrap/app.php 文件中使用 appendToGroup 方法来实现这一点:

use App\Http\Middleware\First;
use App\Http\Middleware\Second;
 
->withMiddleware(function (Middleware $middleware) {
$middleware->appendToGroup('group-name', [
First::class,
Second::class,
]);
 
$middleware->prependToGroup('group-name', [
First::class,
Second::class,
]);
})

Middleware groups may be assigned to routes and controller actions using the same syntax as individual middleware:
中间件组可以使用与单个中间件相同的语法分配给路由和控制器操作:

Route::get('/', function () {
// ...
})->middleware('group-name');
 
Route::middleware(['group-name'])->group(function () {
// ...
});

Laravel's Default Middleware Groups
Laravel 的默认中间件组

Laravel includes predefined web and api middleware groups that contain common middleware you may want to apply to your web and API routes. Remember, Laravel automatically applies these middleware groups to the corresponding routes/web.php and routes/api.php files:
Laravel 包括预定义的 webapi 中间件组,其中包含您可能希望应用于您的 Web 和 API 路由的常见中间件。请记住,Laravel 会自动将这些中间件组应用于相应的 routes/web.phproutes/api.php 文件:

The web Middleware Group
web 中间件组
Illuminate\Cookie\Middleware\EncryptCookies
Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse
Illuminate\Session\Middleware\StartSession
Illuminate\View\Middleware\ShareErrorsFromSession
Illuminate\Foundation\Http\Middleware\ValidateCsrfToken
Illuminate\Routing\Middleware\SubstituteBindings
The api Middleware Group
api 中间件组
Illuminate\Routing\Middleware\SubstituteBindings

If you would like to append or prepend middleware to these groups, you may use the web and api methods within your application's bootstrap/app.php file. The web and api methods are convenient alternatives to the appendToGroup method:
如果您想要向这些组附加或前置中间件,您可以在应用程序的 bootstrap/app.php 文件中使用 webapi 方法。 webapi 方法是 appendToGroup 方法的便捷替代方案。

use App\Http\Middleware\EnsureTokenIsValid;
use App\Http\Middleware\EnsureUserIsSubscribed;
 
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
EnsureUserIsSubscribed::class,
]);
 
$middleware->api(prepend: [
EnsureTokenIsValid::class,
]);
})

You may even replace one of Laravel's default middleware group entries with a custom middleware of your own:
您甚至可以用自己的自定义中间件替换 Laravel 的默认中间件组条目之一:

use App\Http\Middleware\StartCustomSession;
use Illuminate\Session\Middleware\StartSession;
 
$middleware->web(replace: [
StartSession::class => StartCustomSession::class,
]);

Or, you may remove a middleware entirely:
或者,您可以完全删除一个中间件:

$middleware->web(remove: [
StartSession::class,
]);

Manually Managing Laravel's Default Middleware Groups
手动管理 Laravel 的默认中间件组

If you would like to manually manage all of the middleware within Laravel's default web and api middleware groups, you may redefine the groups entirely. The example below will define the web and api middleware groups with their default middleware, allowing you to customize them as necessary:
如果您想要手动管理 Laravel 默认的 webapi 中间件组中的所有中间件,您可以完全重新定义这些组。下面的示例将使用默认中间件定义 webapi 中间件组,允许您根据需要自定义它们:

->withMiddleware(function (Middleware $middleware) {
$middleware->group('web', [
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
]);
 
$middleware->group('api', [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
// 'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
]);
})

By default, the web and api middleware groups are automatically applied to your application's corresponding routes/web.php and routes/api.php files by the bootstrap/app.php file.
默认情况下, webapi 中间件组会自动应用于您的应用程序对应的 routes/web.phproutes/api.php 文件,由 bootstrap/app.php 文件处理。

Middleware Aliases 中间件别名

You may assign aliases to middleware in your application's bootstrap/app.php file. Middleware aliases allows you to define a short alias for a given middleware class, which can be especially useful for middleware with long class names:
您可以在应用程序的 bootstrap/app.php 文件中为中间件分配别名。中间件别名允许您为给定的中间件类定义一个简短的别名,这对于具有较长类名的中间件特别有用:

use App\Http\Middleware\EnsureUserIsSubscribed;
 
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'subscribed' => EnsureUserIsSubscribed::class
]);
})

Once the middleware alias has been defined in your application's bootstrap/app.php file, you may use the alias when assigning the middleware to routes:
一旦在应用程序的 bootstrap/app.php 文件中定义了中间件别名,您可以在将中间件分配给路由时使用该别名:

Route::get('/profile', function () {
// ...
})->middleware('subscribed');

For convenience, some of Laravel's built-in middleware are aliased by default. For example, the auth middleware is an alias for the Illuminate\Auth\Middleware\Authenticate middleware. Below is a list of the default middleware aliases:
为了方便起见,Laravel 的一些内置中间件默认情况下被别名。例如, auth 中间件是 Illuminate\Auth\Middleware\Authenticate 中间件的别名。以下是默认中间件别名列表:

Alias 别名 Middleware 中间件
auth Illuminate\Auth\Middleware\Authenticate
auth.basic Illuminate\Auth\Middleware\AuthenticateWithBasicAuth
auth.session Illuminate\Session\Middleware\AuthenticateSession
cache.headers Illuminate\Http\Middleware\SetCacheHeaders
can Illuminate\Auth\Middleware\Authorize
guest Illuminate\Auth\Middleware\RedirectIfAuthenticated
password.confirm Illuminate\Auth\Middleware\RequirePassword
precognitive Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests
signed Illuminate\Routing\Middleware\ValidateSignature
subscribed \Spark\Http\Middleware\VerifyBillableIsSubscribed
throttle Illuminate\Routing\Middleware\ThrottleRequests or Illuminate\Routing\Middleware\ThrottleRequestsWithRedis  Illuminate\Routing\Middleware\ThrottleRequestsIlluminate\Routing\Middleware\ThrottleRequestsWithRedis
verified Illuminate\Auth\Middleware\EnsureEmailIsVerified

Sorting Middleware 排序中间件

Rarely, you may need your middleware to execute in a specific order but not have control over their order when they are assigned to the route. In these situations, you may specify your middleware priority using the priority method in your application's bootstrap/app.php file:
很少情况下,您可能需要让中间件按特定顺序执行,但在将它们分配给路由时却无法控制它们的顺序。在这些情况下,您可以使用应用程序的 bootstrap/app.php 文件中的 priority 方法来指定中间件的优先级:

->withMiddleware(function (Middleware $middleware) {
$middleware->priority([
\Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class,
\Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
\Illuminate\Auth\Middleware\Authorize::class,
]);
})

Middleware Parameters 中间件参数

Middleware can also receive additional parameters. For example, if your application needs to verify that the authenticated user has a given "role" before performing a given action, you could create an EnsureUserHasRole middleware that receives a role name as an additional argument.
中间件也可以接收额外的参数。例如,如果您的应用程序需要在执行特定操作之前验证经过身份验证的用户是否具有特定的“角色”,您可以创建一个 EnsureUserHasRole 中间件,该中间件接收角色名称作为额外参数。

Additional middleware parameters will be passed to the middleware after the $next argument:
额外的中间件参数将在 $next 参数之后传递给中间件:

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class EnsureUserHasRole
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string $role): Response
{
if (! $request->user()->hasRole($role)) {
// Redirect...
}
 
return $next($request);
}
 
}

Middleware parameters may be specified when defining the route by separating the middleware name and parameters with a ::
中间件参数可以在定义路由时通过使用 : 来分隔中间件名称和参数来指定

Route::put('/post/{id}', function (string $id) {
// ...
})->middleware('role:editor');

Multiple parameters may be delimited by commas:
多个参数可以用逗号分隔:

Route::put('/post/{id}', function (string $id) {
// ...
})->middleware('role:editor,publisher');

Terminable Middleware 可终止中间件

Sometimes a middleware may need to do some work after the HTTP response has been sent to the browser. If you define a terminate method on your middleware and your web server is using FastCGI, the terminate method will automatically be called after the response is sent to the browser:
有时候中间件可能需要在 HTTP 响应发送到浏览器后进行一些工作。如果在您的中间件上定义了一个 terminate 方法,并且您的 Web 服务器正在使用 FastCGI,那么在响应发送到浏览器后, terminate 方法将会自动被调用:

<?php
 
namespace Illuminate\Session\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class TerminatingMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
 
/**
* Handle tasks after the response has been sent to the browser.
*/
public function terminate(Request $request, Response $response): void
{
// ...
}
}

The terminate method should receive both the request and the response. Once you have defined a terminable middleware, you should add it to the list of routes or global middleware in your application's bootstrap/app.php file.
terminate 方法应该同时接收请求和响应。一旦您定义了一个可终止的中间件,您应该将其添加到路由列表或全局中间件中,放在应用程序的 bootstrap/app.php 文件中。

When calling the terminate method on your middleware, Laravel will resolve a fresh instance of the middleware from the service container. If you would like to use the same middleware instance when the handle and terminate methods are called, register the middleware with the container using the container's singleton method. Typically this should be done in the register method of your AppServiceProvider:
在调用中间件的 terminate 方法时,Laravel 将从服务容器中解析出一个新的中间件实例。如果您希望在调用 handleterminate 方法时使用相同的中间件实例,请使用容器的 singleton 方法在容器中注册中间件。通常应该在 AppServiceProviderregister 方法中完成此操作。

use App\Http\Middleware\TerminatingMiddleware;
 
/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}