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

# 控制器的 PHP 属性

> 介绍如何使用 Laravel 13 新增的 #[Middleware]、#[WithoutMiddleware]、#[Authorize] 属性，以声明式方式为控制器配置中间件与授权。

## 概述

在 Laravel 13 中，可以通过 PHP 属性来声明控制器的中间件分配和授权检查。无需再使用传统的 `middleware()` 方法或 `can` 中间件，只需直接在类或方法上添加属性即可完成配置。

```php theme={null}
use Illuminate\Routing\Attributes\Controllers\Authorize;
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class PostController
{
    #[Middleware('subscribed')]
    #[Authorize('create', Post::class)]
    public function store(Request $request): Response
    {
        // ...
    }
}
```

<Tip>
  所有控制器属性都位于 `Illuminate\Routing\Attributes\Controllers` 命名空间下。
</Tip>

## `#[Middleware]` — 分配中间件

### 应用于类

在类级别添加 `#[Middleware]` 后，该中间件将应用于该控制器的所有操作。

```php theme={null}
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class UserController
{
    public function index(): View { /* ... */ }
    public function show(User $user): View { /* ... */ }
    public function store(Request $request): Response { /* ... */ }
}
```

如需添加多个中间件，可重复使用该属性。

```php theme={null}
#[Middleware('auth')]
#[Middleware('verified')]
class ProfileController
{
    // ...
}
```

### 应用于方法

在方法级别添加的中间件会与类级别的中间件合并。

```php theme={null}
#[Middleware('auth')]
class UserController
{
    // 所有人只应用 auth
    public function index(): View { /* ... */ }

    // 同时应用 auth + subscribed
    #[Middleware('subscribed')]
    public function create(): View { /* ... */ }
}
```

### 使用 `only` / `except` 进行筛选

在类级别的属性上指定 `only` 或 `except` 可以对目标方法进行筛选。

```php theme={null}
#[Middleware('auth')]
#[Middleware('subscribed', only: ['create', 'store', 'edit', 'update'])]
class ArticleController
{
    // 仅 auth
    public function index(): View { /* ... */ }
    public function show(Article $article): View { /* ... */ }

    // auth + subscribed
    public function create(): View { /* ... */ }
    public function store(Request $request): Response { /* ... */ }
    public function edit(Article $article): View { /* ... */ }
    public function update(Request $request, Article $article): Response { /* ... */ }

    // 仅 auth（相当于被 except 排除的情况）
    public function destroy(Article $article): Response { /* ... */ }
}
```

### 闭包中间件

属性也支持传入闭包，方便内联编写处理逻辑。

```php theme={null}
use Closure;
use Illuminate\Http\Request;
use Illuminate\Routing\Attributes\Controllers\Middleware;

class ReportController
{
    #[Middleware(static function (Request $request, Closure $next) {
        if (! $request->user()->hasRole('analyst')) {
            abort(403);
        }

        return $next($request);
    })]
    public function generate(): Response
    {
        // ...
    }
}
```

### 与传统 `middleware()` 方法的对比

```php theme={null}
// 传统写法（实现 HasMiddleware 接口）
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;

class UserController implements HasMiddleware
{
    public static function middleware(): array
    {
        return [
            'auth',
            new Middleware('log', only: ['index']),
            new Middleware('subscribed', except: ['store']),
        ];
    }
}

// 使用属性的写法
#[Middleware('auth')]
#[Middleware('log', only: ['index'])]
#[Middleware('subscribed', except: ['store'])]
class UserController
{
    // 无需实现 HasMiddleware
}
```

<Info>
  使用属性时无需实现 `HasMiddleware` 接口。但如果将 `middleware()` 方法与属性混合使用，可能会产生非预期的行为，因此建议只使用其中一种方式。
</Info>

## `#[WithoutMiddleware]` — 排除中间件

从特定方法或整个类中排除在类级别应用的中间件。

### 应用于方法

```php theme={null}
use App\Http\Middleware\EnsureTokenIsValid;
use Illuminate\Routing\Attributes\Controllers\Middleware;
use Illuminate\Routing\Attributes\Controllers\WithoutMiddleware;

#[Middleware('auth')]
#[Middleware(EnsureTokenIsValid::class)]
class ApiController
{
    // 同时应用 auth 和 EnsureTokenIsValid
    public function show(Resource $resource): JsonResponse { /* ... */ }

    // 应用 auth，但排除 EnsureTokenIsValid
    #[WithoutMiddleware(EnsureTokenIsValid::class)]
    public function index(): JsonResponse { /* ... */ }
}
```

### 应用于类与 `only` / `except`

在类级别添加 `#[WithoutMiddleware]` 后，中间件会从包含子类在内的所有操作中被排除。可以通过 `only` / `except` 限定排除范围。

```php theme={null}
#[Middleware('auth')]
#[WithoutMiddleware('subscribed', except: ['index'])]
class AdminController
{
    // 应用 subscribed（未被 except: ['index'] 排除）
    public function index(): View { /* ... */ }

    // 排除 subscribed
    public function dashboard(): View { /* ... */ }
    public function settings(): View { /* ... */ }
}
```

<Warning>
  `#[WithoutMiddleware]` 仅对**路由中间件**有效。无法排除注册在 `app/Http/Kernel.php` 中的全局中间件。
</Warning>

## `#[Authorize]` — 基于策略的授权

作为 `can` 中间件的语法糖，可以通过属性以声明式方式进行基于策略的授权检查。

### 基本用法

```php theme={null}
use App\Models\Post;
use Illuminate\Routing\Attributes\Controllers\Authorize;

class PostController
{
    // 检查 'viewAny' 能力（Post 策略的 viewAny 方法）
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

    // 检查 'view' 能力（传入路由参数 'post'）
    #[Authorize('view', 'post')]
    public function show(Post $post): View { /* ... */ }

    // 检查 'create' 能力
    #[Authorize('create', Post::class)]
    public function create(): View { /* ... */ }

    #[Authorize('create', Post::class)]
    public function store(Request $request): Response { /* ... */ }

    // 检查 'update' 能力（传入路由参数）
    #[Authorize('update', 'post')]
    public function edit(Post $post): View { /* ... */ }

    #[Authorize('update', 'post')]
    public function update(Request $request, Post $post): Response { /* ... */ }

    #[Authorize('delete', 'post')]
    public function destroy(Post $post): Response { /* ... */ }
}
```

### 策略的参数

第二个参数可以传入以下值：

| 传入的值                       | 说明                            |
| -------------------------- | ----------------------------- |
| `Post::class`              | 模型类（用于 `viewAny` 等不需要模型实例的情况） |
| `'post'`                   | 路由参数名（会传入经过模型绑定的实例）           |
| `[Comment::class, 'post']` | 多个参数（以数组形式传给策略方法）             |

```php theme={null}
use App\Models\Comment;
use App\Models\Post;
use Illuminate\Routing\Attributes\Controllers\Authorize;

class CommentController
{
    // 将 $post（路由参数）和 Comment 类传给策略
    #[Authorize('create', [Comment::class, 'post'])]
    public function store(Post $post, Request $request): Response
    {
        // ...
    }

    #[Authorize('delete', 'comment')]
    public function destroy(Comment $comment): Response
    {
        // ...
    }
}
```

### 与 `can` 中间件的对比

```php theme={null}
// 传统写法（Route 门面）
Route::get('/posts', [PostController::class, 'index'])->middleware('can:viewAny,App\Models\Post');
Route::put('/posts/{post}', [PostController::class, 'update'])->middleware('can:update,post');

// 使用属性的写法
class PostController
{
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

    #[Authorize('update', 'post')]
    public function update(Request $request, Post $post): Response { /* ... */ }
}
```

无需在路由文件中编写中间件，授权逻辑将集中在控制器中。

## 实践示例

### 应用于资源控制器

```php theme={null}
use App\Models\Article;
use Illuminate\Routing\Attributes\Controllers\Authorize;
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class ArticleController
{
    #[Authorize('viewAny', Article::class)]
    public function index(): View
    {
        return view('articles.index', [
            'articles' => Article::paginate(),
        ]);
    }

    #[Authorize('view', 'article')]
    public function show(Article $article): View
    {
        return view('articles.show', compact('article'));
    }

    #[Authorize('create', Article::class)]
    public function create(): View
    {
        return view('articles.create');
    }

    #[Authorize('create', Article::class)]
    public function store(StoreArticleRequest $request): RedirectResponse
    {
        $article = Article::create($request->validated());

        return redirect()->route('articles.show', $article);
    }

    #[Authorize('update', 'article')]
    public function edit(Article $article): View
    {
        return view('articles.edit', compact('article'));
    }

    #[Authorize('update', 'article')]
    public function update(UpdateArticleRequest $request, Article $article): RedirectResponse
    {
        $article->update($request->validated());

        return redirect()->route('articles.show', $article);
    }

    #[Authorize('delete', 'article')]
    public function destroy(Article $article): RedirectResponse
    {
        $article->delete();

        return redirect()->route('articles.index');
    }
}
```

### 应用于 API 控制器

```php theme={null}
use Illuminate\Routing\Attributes\Controllers\Middleware;
use Illuminate\Routing\Attributes\Controllers\WithoutMiddleware;

#[Middleware('auth:sanctum')]
#[Middleware('throttle:api')]
class ApiPostController
{
    // 列表获取无需认证
    #[WithoutMiddleware('auth:sanctum')]
    public function index(): JsonResponse
    {
        return response()->json(Post::paginate());
    }

    public function store(Request $request): JsonResponse
    {
        // 仅限已认证用户
    }
}
```

## 属性的处理顺序

当添加了多个属性时，处理顺序如下：

```mermaid theme={null}
flowchart LR
    A["请求"] --> B["类级别的<br>#[Middleware]"]
    B --> C["方法级别的<br>#[Middleware]"]
    C --> D["#[WithoutMiddleware]<br>的排除处理"]
    D --> E["#[Authorize]<br>的授权检查"]
    E --> F["控制器<br>方法执行"]
```

<Info>
  `#[Authorize]` 在内部作为 `can` 中间件运行，因此与 `#[Middleware]` 在同一管道中被处理。
</Info>

## 总结：应该使用哪种方式

| 场景                       | 推荐方案                       |
| ------------------------ | -------------------------- |
| 新建控制器（Laravel 13）        | 使用属性                       |
| 已有 `middleware()` 方法的控制器 | 逐步迁移到属性                    |
| 需要动态的中间件配置               | 继续使用 `middleware()` 方法     |
| 希望在路由文件中统一管理             | 继续使用 `Route::middleware()` |

## 后续步骤

<Columns cols={2}>
  <Card title="PHP 属性（队列 / Eloquent）" icon="code" href="/zh-CN/advanced/php-attributes">
    介绍可用于队列任务和 Eloquent 模型的 PHP 属性。
  </Card>

  <Card title="进阶：控制器" icon="layer-group" href="/zh-CN/controllers">
    学习 Laravel 控制器的基础用法。
  </Card>
</Columns>


## Related topics

- [控制器](/zh-CN/controllers.md)
- [Laravel 13 新功能汇总](/zh-CN/blog/laravel-13-new-features.md)
- [路由](/zh-CN/routing.md)
- [2026 年 3 月 Laravel 更新](/zh-CN/blog/changelog/202603.md)
- [事件与监听器](/zh-CN/events.md)
