> ## 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 13 的 Router 實作出發，說明套件中介層別名的註冊、明確套用、避免覆寫群組，以及快取時的驗證。

為套件加入 HTTP 功能時，應將「讓中介層可供使用」與「將其套用到使用端應用程式的請求」分開處理。若設計成僅安裝套件並不會改變所有路由的行為，就更容易管理導入時的影響與未來的相容性。

本頁以[套件開發基礎](/zh-TW/advanced/package-development)為前提，參照 Laravel 官方文件 `13.x` 與 Framework `v13.36.0` 的實作。一般中介層的寫法請參閱[中介層](/zh-TW/middleware)。

## 將註冊、套用與執行分開

`Router::aliasMiddleware()` 會以別名為鍵、類別名稱為值，註冊到 Router 的陣列中。它不會建立中介層的實例，也不會將其套用到路由。

```mermaid theme={null}
flowchart TD
    A["提供者的 boot"] --> B["以 aliasMiddleware<br>註冊名稱與類別"]
    C["路由定義"] --> D["以 middleware<br>指定要套用的名稱"]
    B --> E["決定請求的路由"]
    D --> E
    E --> F["展開別名與群組<br>反映排除與優先順序"]
    F --> G["Pipeline 執行中介層"]
```

| 操作 | 角色 | 單憑此操作不會進行的事 |
| - | - | - |
| `aliasMiddleware()` | 註冊名稱到類別的對應 | 套用到請求 |
| 路由的 `middleware()` | 指派給目標路由 | 中介層類別的自訂實作 |
| `middlewareGroup()` | 設定具名群組的內容 | 加入全域中介層 |
| 加入 HTTP Kernel 的全域堆疊 | 以所有請求為對象 | 支援以路由為單位的排除 |

別名名稱是公開 API。請避免使用 `auth` 或 `throttle` 等內建名稱，以及容易與其他套件衝突的通用名稱，並加上套件專屬的前綴。若對相同的鍵重新註冊，`aliasMiddleware()` 會覆寫先前的對應。

## 在提供者中公開別名

以下範例建立一個在回應中加上固定識別標頭的中介層。這並不是驗證、授權或存取控制的範例。前提是透過 Composer 的 PSR-4 將 `Acme\Courier\` 對應到 `src/`，並以[自動探索](/zh-TW/advanced/package-discovery)或手動方式註冊提供者。

```php src/Http/Middleware/AddCourierHeader.php theme={null}
<?php

namespace Acme\Courier\Http\Middleware;

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

class AddCourierHeader
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        $response->headers->set('X-Acme-Courier', 'enabled');

        return $response;
    }
}
```

Router 的使用在 `boot()` 中進行。`boot()` 的參數會由服務容器注入，因此不需要為了註冊中介層而自動編輯使用者的 `bootstrap/app.php`。

```php src/CourierServiceProvider.php theme={null}
<?php

namespace Acme\Courier;

use Acme\Courier\Http\Middleware\AddCourierHeader;
use Illuminate\Routing\Router;
use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(Router $router): void
    {
        $router->aliasMiddleware('acme-courier.header', AddCourierHeader::class);
    }
}
```

請將此提供者作為一般提供者啟動。若將別名註冊移到只有在其他服務被解析時才執行的延遲提供者中，就無法保證在路由需要時已完成註冊。

### 由使用者以路由為單位套用

在使用端應用程式的 `routes/web.php` 中，只指派給需要的路由。

```php routes/web.php theme={null}
<?php

use Illuminate\Support\Facades\Route;

Route::get('/courier-status', function () {
    return response()->json(['available' => true]);
})->middleware('acme-courier.header');
```

也可以不提供別名，而是像 `->middleware(AddCourierHeader::class)` 這樣直接指定類別名稱。請依是否需要為使用者提供簡短名稱來選擇。

即使套件本身提供路由，也要在路由檔案中明確指定所需的中介層。`loadRoutesFrom()` 不會自動加入 `web` 或 `api`。註冊方式與路由快取的處理，請參閱[套件的路由註冊與快取](/zh-TW/advanced/package-routes)。

## 是否套用到整個應用程式由使用者選擇

若想套用到使用端應用程式的所有 `web` 路由，由使用者在既有的 `withMiddleware()` 回呼中加入以下設定。這不是取代既有設定的範例。

```php theme={null}
use Acme\Courier\Http\Middleware\AddCourierHeader;
use Illuminate\Foundation\Configuration\Middleware;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->web(append: [
        AddCourierHeader::class,
    ]);
})
```

此設定直接使用類別名稱，因此不依賴別名的註冊順序。適用範圍是 `web` 群組，而不是全域堆疊。它不會自動套用到 `api` 或未帶有任何群組的路由。

<Warning>
  在安裝套件時就套用到整個 `web` 的設計，與由使用者明確套用的設計並不相容。請勿透過自動更新，將會影響驗證、Session、回應標頭、外部通訊等的處理擴及所有路由。
</Warning>

## Router 的群組操作存在差異

套件若要提供自己的群組，請使用專屬的名稱。以下是放在提供者 `boot(Router $router)` 內的範例。

```php theme={null}
$router->middlewareGroup('acme-courier', [
    AddCourierHeader::class,
]);
```

使用者可以用 `->middleware('acme-courier')` 套用。不過，此群組只包含所指定的類別，並不會繼承 `web` 的 Session 或 CSRF 保護。

也有加入既有群組的 API，但在 `v13.36.0` 的 Router 實作中有以下差異。

| Router 的方法 | 群組存在時 | 群組不存在時 |
| - | - | - |
| `middlewareGroup($name, $items)` | 取代整個陣列 | 以指定的陣列建立 |
| `pushMiddlewareToGroup($name, $item)` | 若無相同項目則加到末尾 | 先建立空陣列再加入 |
| `prependMiddlewareToGroup($name, $item)` | 若無相同項目則加到開頭 | 不做任何事 |

若以 `middlewareGroup('web', [...])` 只設定套件的類別，就會取代既有 `web` 的內容。請勿與用於加入的方法混淆。此外，即使對不存在的 `web` 執行 `pushMiddlewareToGroup()`，也不會還原標準的 `web` 群組。

用於加入的方法，其重複判定是針對所註冊的值。指向相同類別的別名與類別名稱是不同的字串，因此在註冊表階段會成為不同的項目。請統一寫法，不要假設以不同寫法表示的相同類別也會在註冊時被去除重複。

### 不要只靠註冊順序保證執行順序

Router 會展開指派給路由的中介層別名與群組，處理排除後再依優先順序排序。僅靠加到開頭或末尾，並無法保證與驗證或路由模型綁定之間嚴格的前後關係。

此外，HTTP Kernel 的 `syncMiddlewareToRouter()` 會以 `middlewareGroup()` 將 Kernel 所持有的群組同步到 Router。若 Kernel 端的設定之後被重新同步，直接加入 Router 的群組內容可能會被取代。基本方針是不要在套件之間以啟動順序競相修改既有群組，而是在使用端應用程式的 `withMiddleware()` 中集中設定。

對於需要「Session 開始後」、「驗證後」等順序的功能，請在安裝步驟中明確寫出該前提，並搭配使用端應用程式的優先順序設定進行確認。請避免由套件整個取代應用程式的優先順序清單。

## 快取與驗證的邊界

請勿將別名註冊放在 `routesAreCached()` 的條件內或路由檔案中。有快取時 `loadRoutesFrom()` 不會載入路由檔案，但為了在執行時解析快取路由中保留的別名，仍需要進行註冊。請在提供者的 `boot()` 中每次都註冊。

變更路由的指派後，請在使用端應用程式中重新建立路由快取。

```bash theme={null}
php artisan route:cache
php artisan route:list --path=courier-status -vv
```

`-vv` 用於展開並確認群組的內容。不過，即使清單顯示成功，也不能保證中介層內的處理或 Session 的使用都正確。仍需要進行 HTTP 請求的測試。

以下是要加入定義了上述 `/courier-status` 的 Laravel 應用程式 Feature 測試中的方法。

```php theme={null}
public function test_courier_header_is_applied(): void
{
    $this->get('/courier-status')
        ->assertOk()
        ->assertHeader('X-Acme-Courier', 'enabled');
}
```

| 確認案例 | 預期結果 |
| - | - |
| 只註冊了別名的路由 | 不會加上標頭 |
| 指派了別名的路由 | 會加上標頭 |
| 直接指派類別名稱的路由 | 行為相同 |
| 建立快取後啟動新的應用程式 | 相同路由有相同行為 |
| 選擇套用到整個 `web` | 套用到目標 `web` 路由，且不擴及非目標路由 |
| 設定使用端應用程式自己的群組與優先順序 | 與 Session、驗證、綁定之間的順序符合需求 |

檢查群組是否存在或別名陣列的單元測試，與實際的 HTTP 執行是不同的驗證。特別是快取驗證，不應只是查看同一程序中的 Router，而要在建立快取後以新的啟動進行。若使用長時間執行的 worker，請依照一般的部署步驟重新載入。

## 相關頁面

<Columns cols={2}>
  <Card title="中介層" icon="layer-group" href="/zh-TW/middleware">
    確認建立、套用到路由、參數與優先順序的基礎。
  </Card>

  <Card title="套件的路由註冊與快取" icon="route" href="/zh-TW/advanced/package-routes">
    確認路由載入與建立快取時設定之間的關係。
  </Card>
</Columns>

## 參考的一手資料

* [Laravel 官方文件：中介層](https://github.com/laravel/docs/blob/6bb0a3be3c623f7964c8277625a07b6df7f1de2e/middleware.md)
* [Laravel 官方文件：服務提供者的 boot 與依賴注入](https://github.com/laravel/docs/blob/6bb0a3be3c623f7964c8277625a07b6df7f1de2e/providers.md#the-boot-method)
* [Router：別名註冊、群組操作、中介層的展開與排序](https://github.com/laravel/framework/blob/v13.36.0/src/Illuminate/Routing/Router.php)
* [HTTP Kernel：syncMiddlewareToRouter](https://github.com/laravel/framework/blob/v13.36.0/src/Illuminate/Foundation/Http/Kernel.php)


## Related topics

- [Laravel 套件開發](/zh-TW/advanced/package-development.md)
- [套件的路由註冊與快取](/zh-TW/advanced/package-routes.md)
- [套件的事件監聽器註冊與快取](/zh-TW/advanced/package-events.md)
- [套件的 Artisan 指令註冊與相依性解析](/zh-TW/advanced/package-commands.md)
- [Controller 的 PHP Attributes](/zh-TW/advanced/controller-attributes.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.