> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 中间件

> 介绍如何使用 Laravel 中间件对 HTTP 请求进行过滤与预处理。

## 什么是中间件

中间件是一种检查、过滤到达应用的 HTTP 请求的机制。
你可以在请求到达控制器前后插入处理逻辑。

比如 Laravel 内置了认证中间件。
当用户未认证时，会重定向到登录页面；若已认证，则允许请求继续进入应用内部。

除认证外，还可以为日志记录、CSRF 保护、限流等各种目的编写中间件。关于 Laravel 13 CSRF 保护的详细内容，请参阅 [CSRF 保护](/zh/csrf)。

<Info>
  用户自定义的中间件通常放在 `app/Http/Middleware` 目录下。
</Info>

```mermaid theme={null}
flowchart TD
    A["HTTP 请求"] --> B["全局中间件<br>(应用于所有请求)"]
    B --> C["路由中间件<br>(应用于特定路由)"]
    C --> D["控制器 / 闭包"]
    D --> E["生成响应"]
    E --> F["路由中间件<br>(后处理)"]
    F --> G["全局中间件<br>(后处理)"]
    G --> H["HTTP 响应"]
```

## 内置中间件

Laravel 默认准备了 `web` 与 `api` 两个中间件组。
`routes/web.php` 会自动应用 `web` 组，`routes/api.php` 会自动应用 `api` 组。

| `web` 中间件组                       |
| -------------------------------- |
| `EncryptCookies`                 |
| `AddQueuedCookiesToResponse`     |
| `StartSession`                   |
| `ShareErrorsFromSession`         |
| `PreventRequestForgery`（CSRF 保护） |
| `SubstituteBindings`             |

对常用中间件，还设置了别名，可以用简短的名字引用。

| 别名         | 中间件                                                  |
| ---------- | ---------------------------------------------------- |
| `auth`     | `Illuminate\Auth\Middleware\Authenticate`            |
| `guest`    | `Illuminate\Auth\Middleware\RedirectIfAuthenticated` |
| `verified` | `Illuminate\Auth\Middleware\EnsureEmailIsVerified`   |
| `throttle` | `Illuminate\Routing\Middleware\ThrottleRequests`     |

## 创建中间件

使用 `make:middleware` Artisan 命令创建新的中间件。

```shell theme={null}
php artisan make:middleware EnsureTokenIsValid
```

会生成 `app/Http/Middleware/EnsureTokenIsValid.php`。
在 `handle` 方法中编写处理请求的逻辑。

```php theme={null}
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureTokenIsValid
{
    /**
     * 处理传入的请求
     *
     * @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);
    }
}
```

调用 `$next($request)` 会将请求交给应用的下一环处理。
若不满足条件，可以返回重定向或响应，从而阻止请求继续。

### 请求前后的处理

中间件可以在请求**之前**或**之后**执行逻辑。

```php theme={null}
// 在请求前处理
public function handle(Request $request, Closure $next): Response
{
    // 前置处理写在这里

    return $next($request);
}
```

```php theme={null}
// 在响应后处理
public function handle(Request $request, Closure $next): Response
{
    $response = $next($request);

    // 后置处理写在这里

    return $response;
}
```

## 注册中间件

### 全局中间件

若希望所有请求都执行该中间件，请在 `bootstrap/app.php` 的 `withMiddleware` 中添加到全局栈。

```php theme={null}
// bootstrap/app.php
use App\Http\Middleware\EnsureTokenIsValid;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->append(EnsureTokenIsValid::class);
    });
```

`append` 追加到栈末尾。要放到最前面请用 `prepend`。

### 对路由应用中间件

若只对特定路由应用中间件，可在路由定义上调用 `middleware`。

```php theme={null}
use App\Http\Middleware\EnsureTokenIsValid;

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

要应用多个中间件，用数组传入。

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

若要在特定路由上排除某个中间件，可使用 `withoutMiddleware`。

```php theme={null}
use App\Http\Middleware\EnsureTokenIsValid;

Route::middleware([EnsureTokenIsValid::class])->group(function () {
    Route::get('/', function () {
        // 应用了该中间件
    });

    Route::get('/profile', function () {
        // 该路由排除中间件
    })->withoutMiddleware([EnsureTokenIsValid::class]);
});
```

### 中间件别名

可以为较长的类名设置短别名。在 `bootstrap/app.php` 中配置。

```php theme={null}
// bootstrap/app.php
use App\Http\Middleware\EnsureUserIsSubscribed;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->alias([
            'subscribed' => EnsureUserIsSubscribed::class,
        ]);
    });
```

使用别名应用到路由：

```php theme={null}
Route::get('/profile', function () {
    // ...
})->middleware('subscribed');
```

## 中间件组

将多个中间件归为一个键，便于统一应用到路由。
在 `bootstrap/app.php` 中使用 `appendToGroup` 或 `prependToGroup`。

```php theme={null}
// bootstrap/app.php
use App\Http\Middleware\First;
use App\Http\Middleware\Second;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->appendToGroup('group-name', [
            First::class,
            Second::class,
        ]);
    });
```

分组可像普通中间件一样应用到路由。

```php theme={null}
Route::get('/', function () {
    // ...
})->middleware('group-name');

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

要向已有的 `web` 或 `api` 分组添加中间件，可使用专用方法。

```php theme={null}
// bootstrap/app.php
use App\Http\Middleware\EnsureUserIsSubscribed;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->web(append: [
            EnsureUserIsSubscribed::class,
        ]);
    });
```

## 中间件参数

中间件可以接收额外参数。
在 `handle` 方法的 `$next` 参数之后添加参数即可。

```php theme={null}
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureUserHasRole
{
    public function handle(Request $request, Closure $next, string $role): Response
    {
        if (! $request->user()->hasRole($role)) {
            return redirect('/home');
        }

        return $next($request);
    }
}
```

在路由定义中，中间件名和参数用 `:` 分隔。

```php theme={null}
use App\Http\Middleware\EnsureUserHasRole;

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

多个参数用逗号分隔。

```php theme={null}
Route::put('/post/{id}', function (string $id) {
    // ...
})->middleware(EnsureUserHasRole::class.':editor,publisher');
```

## 实用示例：认证检查中间件

一个简单的示例：把未登录用户重定向到登录页。

```shell theme={null}
php artisan make:middleware RedirectIfNotAuthenticated
```

```php theme={null}
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class RedirectIfNotAuthenticated
{
    public function handle(Request $request, Closure $next): Response
    {
        if (! $request->user()) {
            return redirect('/login');
        }

        return $next($request);
    }
}
```

应用到路由：

```php theme={null}
Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(RedirectIfNotAuthenticated::class);
```

<Tip>
  Laravel 已内置用于认证的 `auth` 中间件。自行实现前请先确认现有中间件能否满足需求。
</Tip>

## 下一步

<Card title="HTTP 请求" icon="globe" href="/zh/requests">
  学习如何在控制器中接收请求数据。
</Card>


## Related topics

- [Laravel AI SDK](/zh/ai-sdk.md)
- [Laravel Folio](/zh/folio.md)
- [OAuth 2.0 认证 - Google Sheets API for Laravel](/zh/packages/laravel-google-sheets/oauth.md)
- [Laravel Pennant](/zh/pennant.md)
- [Laravel Sentinel — 路由保护中间件调查](/zh/blog/sentinel-introduction.md)
