> ## 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 的实现，讲解 loadRoutesFrom 的作用、中间件与名称的分离，以及将配置变更反映到路由缓存的步骤。

当包提供 HTTP 端点时，路由不仅要在开发环境中正常工作，还需要在使用方应用程序创建路由缓存之后，依然按照同样的约定工作。如果设计上允许通过配置修改 URL 前缀或启用与禁用状态，还需要告知用户这些变更何时生效。

本页以[包开发基础](/zh-CN/advanced/package-development)为前提，将注册处理与缓存的生命周期分开讨论。官方文档参考的是 Laravel 13 的默认分支 `13.x`，框架实现参考的是最新发布版本 `v13.34.0`。

## loadRoutesFrom 只负责加载文件

当应用程序实现了 `CachesRoutes` 且 `routesAreCached()` 为真时，`ServiceProvider::loadRoutesFrom()` 不会加载路由文件。其他情况下，它会 `require` 指定的文件。

该方法本身不会添加 URI 或路由名称的前缀、控制器命名空间以及中间件。它也不会发布文件，或向现有缓存中追加路由。

```mermaid theme={null}
flowchart TD
    A["提供者的 boot"] --> B["调用 loadRoutesFrom"]
    B --> C{"存在路由缓存?"}
    C -->|否| D["require 包的路由文件"]
    C -->|是| E["跳过包文件的加载"]
    E --> F["Laravel 的 RouteServiceProvider<br>加载应用程序的缓存"]
```

图示以标准的 Laravel 应用程序为前提。并不存在包专用的缓存，包的路由也包含在整个应用程序的路由缓存中。

<Warning>
  即使将包内的文件命名为 `routes/web.php`，也不会因此自动附加 `web` 中间件。它的加载路径与应用程序端的标准路由文件不同，因此请在包中显式指定所需的中间件。
</Warning>

## 将配置与注册分开

下面的示例创建一个返回包是否可响应的公开端点。前提是已通过 Composer 的 PSR-4 将 `Acme\Courier\` 映射到 `src/`，并且提供者已通过[自动发现](/zh-CN/advanced/package-discovery)或手动方式注册。

```php config/courier.php theme={null}
<?php

return [
    'routes' => [
        'enabled' => true,
        'prefix' => 'acme-courier',
    ],
];
```

配置的合并在 `register()` 中进行，路由的加载在 `boot()` 中进行。请不要将注册 HTTP 路由的提供者设为 `DeferrableProvider`，因为这样就无法保证在需要路由时提供者已经启动。

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

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(__DIR__.'/../config/courier.php', 'courier');
    }

    public function boot(): void
    {
        if (config('courier.routes.enabled')) {
            $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
        }
    }
}
```

该条件只控制路由的注册。如果提供者还注册了其他服务或视图，请不要将它们放在该条件内部。关于向用户发布配置的方法以及合并嵌套配置时的注意事项，请参阅[包配置的合并与缓存](/zh-CN/advanced/package-config-merging)。

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

use Acme\Courier\Http\Controllers\StatusController;
use Illuminate\Support\Facades\Route;

Route::middleware('web')
    ->prefix(config('courier.routes.prefix'))
    ->name('acme-courier.')
    ->group(function () {
        Route::get('/status', StatusController::class)->name('status');
    });
```

```php src/Http/Controllers/StatusController.php theme={null}
<?php

namespace Acme\Courier\Http\Controllers;

use Illuminate\Http\JsonResponse;

class StatusController
{
    public function __invoke(): JsonResponse
    {
        return response()->json(['available' => true]);
    }
}
```

默认的 URI 为 `/acme-courier/status`，路由名称为 `acme-courier.status`。只要使用 `route('acme-courier.status')` 生成 URL，即使修改了 URI 前缀，调用方也可以继续使用同一个路由名称。路由组的 `name()` 会直接拼接字符串，因此末尾的 `.` 也需要指定。

<Info>
  `web` 并不能替代认证与授权。本示例是不包含敏感信息的公开端点。对于返回用户数据的端点，请根据需求另行设置认证中间件和授权处理。
</Info>

## 分别防止 URI 与路由名称的冲突

URI 前缀与路由名称前缀是两套不同的机制。只添加其中一种，无法防止另一种发生冲突。

| 对象 | 本示例的设计 | 维护上的注意事项 |
| - | - | - |
| URI | 默认为 `acme-courier`，可通过配置修改 | 选择不会与使用方应用程序现有 URL 冲突的值 |
| 路由名称 | 固定为 `acme-courier.` | 使用包特有的名称，并作为 URL 生成的约定加以维持 |
| 控制器 | 使用类引用 | 不依赖应用程序端的控制器命名空间 |
| 中间件 | 显式指定 `web` | 结合目标应用程序的中间件配置进行确认 |

`AbstractRouteCollection` 在创建用于缓存的路由集合时，如果不同的路由使用了相同的名称，会抛出 `LogicException`。"正常启动时能够生成 URL"并不能保证可以缓存。即使是 URI 不同的两个路由，只要名称相同也会出现问题。

请不要把通过注册顺序覆盖使用方应用程序的路由作为包的扩展方式。如有需要，应提供禁用路由的配置，以及用户可以从其他路由调用的服务。

## 创建缓存时的配置会保留在路由定义中

`RouteCacheCommand` 会先执行 `route:clear`，然后启动一个新的应用程序来收集路由。接着将这些路由准备为可序列化的形式，并把编译结果写入缓存文件。

此时包的路由文件也会被加载，因此前缀以及是否注册由**创建缓存时的配置**决定。之后的启动中，`loadRoutesFrom()` 不会加载文件，而是使用已缓存的路由。

| 变更 | 旧路由缓存残留时 | 所需操作 |
| - | - | - |
| 向包中添加路由 | 新增的路由不会出现 | 重新创建路由缓存 |
| 修改 `routes.prefix` | 原来的 URI 仍然保留 | 使用新配置重新创建 |
| 将 `routes.enabled` 改为 `false` | 缓存中的路由不会消失 | 使用禁用后的配置重新创建 |
| 删除包 | 可能残留引用已删除类的定义 | 按删除后的结构重新创建 |

<Warning>
  `routes.enabled` 是控制注册的配置，而不是针对每个请求的访问拒绝。在旧缓存残留的状态下仅禁用配置，并不意味着已经停止了该端点。
</Warning>

请不要根据用户或租户等每个请求都会变化的条件来注册路由。这些条件会在创建缓存时的 CLI 环境中求值。路由应以稳定的结构注册，是否允许访问则由中间件或控制器内的授权来判断。

### 部署时先确定配置

在完成代码与配置的更新后，对于使用配置缓存的结构，请按以下顺序重新创建。请将其纳入使用方应用程序的部署流程。

```bash theme={null}
php artisan config:cache
php artisan route:cache
php artisan route:list --name=acme-courier -vv
```

如果在旧配置缓存残留的情况下执行 `route:cache`，路由也会基于旧配置创建。仅重新执行 `config:cache` 并不会更新路由缓存。通过 `-vv` 还可以确认中间件组的内容。

在开发过程中需要在无缓存的状态下确认行为时，请根据需要同时清除两者。

```bash theme={null}
php artisan config:clear
php artisan route:clear
```

在存在缓存的启动中，路由文件不会被执行。如果在其中注册事件监听器或容器绑定，行为就会发生变化，因此请不要让路由文件承担路由定义以外的副作用。在使用长时间运行进程的环境中，还应将缓存更新后的重新加载纳入常规部署流程。

## 发布前需要确认的组合

除了包的测试之外，还需要在 Laravel 13 的使用方应用程序中确认以下组合。不仅要覆盖内存中的路由注册，也要覆盖 Artisan 启动新应用程序的路径。

* 无缓存时 `/acme-courier/status` 能够响应，且路由名称与中间件符合预期。
* `route:cache` 执行成功，在新的启动中也以相同的 URI 和路由名称响应。
* 修改前缀并重新创建缓存后，新的 URI 能够响应，旧 URI 上的包路由不再存在。
* 禁用并重新创建缓存后，`route:list --name=acme-courier` 中不再显示目标路由。
* 与使用方应用程序或其他包之间不存在 URI 或路由名称的冲突。

如果也确认保留旧缓存的情况，就能够重现"明明修改了配置文件，URL 却没有变化"这类用户反馈。请在升级步骤中明确写出重新创建缓存，并将路由名称和中间件的变更也纳入兼容性的考量范围。

## 相关页面

<Columns cols={2}>
  <Card title="路由" icon="route" href="/zh-CN/routing">
    了解路由组、命名路由和列表显示的基础知识。
  </Card>

  <Card title="包配置的合并与缓存" icon="sliders" href="/zh-CN/advanced/package-config-merging">
    了解兼顾已发布配置和配置缓存的更新步骤。
  </Card>

  <Card title="延迟 Service Provider" icon="clock" href="/zh-CN/advanced/deferred-provider">
    了解为什么不应延迟注册路由的提供者。
  </Card>

  <Card title="包的版本兼容性管理" icon="code-branch" href="/zh-CN/advanced/package-versioning">
    将公开 API 的变更与发布策略和持续验证关联起来。
  </Card>
</Columns>

## 参考的一手资料

* [Laravel 官方文档：包的路由](https://github.com/laravel/docs/blob/156fc7fde114548640e13c39aa79b991291c3f91/packages.md#routes)
* [Laravel 官方文档：路由](https://github.com/laravel/docs/blob/156fc7fde114548640e13c39aa79b991291c3f91/routing.md)
* [ServiceProvider：loadRoutesFrom 的实现](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Support/ServiceProvider.php)
* [RouteServiceProvider：缓存的加载](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Support/Providers/RouteServiceProvider.php)
* [RouteCacheCommand：在新应用程序中收集与保存](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/RouteCacheCommand.php)
* [AbstractRouteCollection：路由名称的重复检测](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Routing/AbstractRouteCollection.php)


## Related topics

- [Laravel 包开发](/zh-CN/advanced/package-development.md)
- [进阶主题](/zh-CN/advanced/index.md)
- [包配置的合并与缓存](/zh-CN/advanced/package-config-merging.md)
- [路由](/zh-CN/routing.md)
- [Laravel 12 升级到 13 指南](/zh-CN/blog/upgrade-12-to-13.md)


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