路由
基本路由
最基本的 Laravel 路由接受一个 URI 和一个闭包,提供了一种非常简单且富有表现力的方式来定义路由和行为,而无需复杂的路由配置文件:
use Illuminate\Support\Facades\Route;
Route::get('/greeting', function () {
return 'Hello World';
});默认路由文件
所有 Laravel 路由都定义在您的路由文件中,这些文件位于 routes 目录中。这些文件由 Laravel 使用应用程序 bootstrap/app.php 文件中指定的配置自动加载。routes/web.php 文件定义了用于您的 Web 界面的路由。这些路由被分配了 web 中间件组,它提供了会话状态和 CSRF 保护等功能。
对于大多数应用程序,您将从在 routes/web.php 文件中定义路由开始。routes/web.php 中定义的路由可以通过在浏览器中输入定义的路由 URL 来访问。例如,您可以通过在浏览器中导航到 http://example.com/user 来访问以下路由:
use App\Http\Controllers\UserController;
Route::get('/user', [UserController::class, 'index']);API 路由
如果您的应用程序还将提供无状态 API,您可以使用 install:api Artisan 命令启用 API 路由:
php artisan install:apiinstall:api 命令安装 Laravel Sanctum,它提供了一个健壮但简单的 API 令牌认证守卫,可用于认证第三方 API 消费者、SPA 或移动应用程序。此外,install:api 命令创建 routes/api.php 文件:
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');当然,您可以自由地在应该公开访问的路由上省略 auth:sanctum 中间件。
routes/api.php 中的路由是无状态的,并被分配给 api 中间件组。此外,/api URI 前缀会自动应用于这些路由,因此您不需要手动将其应用于文件中的每个路由。您可以通过修改应用程序的 bootstrap/app.php 文件来更改前缀:
->withRouting(
api: __DIR__.'/../routes/api.php',
apiPrefix: 'api/admin',
// ...
)可用的路由器方法
路由器允许您注册响应任何 HTTP 动词的路由:
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);有时您可能需要注册响应多个 HTTP 动词的路由。您可以使用 match 方法来实现。或者,您甚至可以使用 any 方法注册响应所有 HTTP 动词的路由:
Route::match(['get', 'post'], '/', function () {
// ...
});
Route::any('/', function () {
// ...
});NOTE
当定义共享相同 URI 的多个路由时,使用 get、post、put、patch、delete 和 options 方法的路由应该在使用 any、match 和 redirect 方法的路由之前定义。这确保传入请求与正确的路由匹配。
依赖注入
您可以在路由的回调签名中类型提示路由所需的任何依赖项。声明的依赖项将由 Laravel 服务容器自动解析并注入到回调中。例如,您可以类型提示 Illuminate\Http\Request 类,以便将当前 HTTP 请求自动注入到您的路由回调中:
use Illuminate\Http\Request;
Route::get('/users', function (Request $request) {
// ...
});CSRF 保护
请记住,任何指向 POST、PUT、PATCH 或 DELETE 路由的 HTML 表单(在 web 路由文件中定义)都应包含 CSRF 令牌字段。否则,请求将被拒绝。您可以在 CSRF 文档中阅读更多关于 CSRF 保护的信息:
<form method="POST" action="/profile">
@csrf
...
</form>重定向路由
如果您正在定义重定向到另一个 URI 的路由,可以使用 Route::redirect 方法。此方法提供了一个方便的快捷方式,这样您就不必定义完整的路由或控制器来执行简单的重定向:
Route::redirect('/here', '/there');默认情况下,Route::redirect 返回 302 状态码。您可以使用可选的第三个参数自定义状态码:
Route::redirect('/here', '/there', 301);或者,您可以使用 Route::permanentRedirect 方法返回 301 状态码:
Route::permanentRedirect('/here', '/there');WARNING
在重定向路由中使用路由参数时,以下参数由 Laravel 保留,不能使用:destination 和 status。
视图路由
如果您的路由只需要返回一个视图,可以使用 Route::view 方法。与 redirect 方法一样,此方法提供了一个简单的快捷方式,这样您就不必定义完整的路由或控制器。view 方法接受 URI 作为第一个参数,视图名称作为第二个参数。此外,您可以提供一个数据数组作为可选的第三个参数传递给视图:
Route::view('/welcome', 'welcome');
Route::view('/welcome', 'welcome', ['name' => 'Taylor']);WARNING
在视图路由中使用路由参数时,以下参数由 Laravel 保留,不能使用:view、data、status 和 headers。
列出您的路由
route:list Artisan 命令可以轻松提供应用程序定义的所有路由的概览:
php artisan route:list默认情况下,分配给每个路由的路由中间件不会显示在 route:list 输出中;但是,您可以通过向命令添加 -v 选项来指示 Laravel 显示路由中间件和中间件组名称:
php artisan route:list -v
# 展开中间件组...
php artisan route:list -vv您也可以指示 Laravel 仅显示以给定 URI 开头的路由:
php artisan route:list --path=api此外,您可以通过在执行 route:list 命令时提供 --except-vendor 选项来指示 Laravel 隐藏由第三方包定义的任何路由:
php artisan route:list --except-vendor同样,您也可以通过在执行 route:list 命令时提供 --only-vendor 选项来指示 Laravel 仅显示由第三方包定义的路由:
php artisan route:list --only-vendor路由自定义
默认情况下,您的应用程序的路由由 bootstrap/app.php 文件配置和加载:
<?php
use Illuminate\Foundation\Application;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)->create();但是,有时您可能希望定义一个全新的文件来包含应用程序路由的子集。为此,您可以向 withRouting 方法提供一个 then 闭包。在此闭包中,您可以注册应用程序所需的任何其他路由:
use Illuminate\Support\Facades\Route;
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
then: function () {
Route::middleware('api')
->prefix('webhooks')
->name('webhooks.')
->group(base_path('routes/webhooks.php'));
},
)或者,您甚至可以通过向 withRouting 方法提供 using 闭包来完全控制路由注册。当传递此参数时,框架不会注册任何 HTTP 路由,您需要手动注册所有路由:
use Illuminate\Support\Facades\Route;
->withRouting(
commands: __DIR__.'/../routes/console.php',
using: function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
},
)路由参数
必选参数
有时您需要在路由中捕获 URI 的片段。例如,您可能需要从 URL 中捕获用户的 ID。您可以通过定义路由参数来实现:
Route::get('/user/{id}', function (string $id) {
return 'User '.$id;
});您可以根据路由需要定义任意数量的路由参数:
Route::get('/posts/{post}/comments/{comment}', function (string $postId, string $commentId) {
// ...
});路由参数始终包含在 {} 花括号内,应由字母字符组成。下划线 (_) 在路由参数名称中也是可接受的。路由参数根据其顺序注入到路由回调/控制器中 - 路由回调/控制器参数的名称无关紧要。
参数和依赖注入
如果您的路由有希望 Laravel 服务容器自动注入到路由回调中的依赖项,您应该在依赖项之后列出路由参数:
use Illuminate\Http\Request;
Route::get('/user/{id}', function (Request $request, string $id) {
return 'User '.$id;
});可选参数
有时您可能需要指定一个可能并不总是存在于 URI 中的路由参数。您可以通过在参数名称后放置 ? 标记来实现。确保为路由的相应变量提供默认值:
Route::get('/user/{name?}', function (?string $name = null) {
return $name;
});
Route::get('/user/{name?}', function (?string $name = 'John') {
return $name;
});正则表达式约束
您可以使用路由实例上的 where 方法约束路由参数的格式。where 方法接受参数名称和定义如何约束参数的正则表达式:
Route::get('/user/{name}', function (string $name) {
// ...
})->where('name', '[A-Za-z]+');
Route::get('/user/{id}', function (string $id) {
// ...
})->where('id', '[0-9]+');
Route::get('/user/{id}/{name}', function (string $id, string $name) {
// ...
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);为了方便,一些常用的正则表达式模式有辅助方法,允许您快速向路由添加模式约束:
Route::get('/user/{id}/{name}', function (string $id, string $name) {
// ...
})->whereNumber('id')->whereAlpha('name');
Route::get('/user/{name}', function (string $name) {
// ...
})->whereAlphaNumeric('name');
Route::get('/user/{id}', function (string $id) {
// ...
})->whereUuid('id');
Route::get('/user/{id}', function (string $id) {
// ...
})->whereUlid('id');
Route::get('/category/{category}', function (string $category) {
// ...
})->whereIn('category', ['movie', 'song', 'painting']);
Route::get('/category/{category}', function (string $category) {
// ...
})->whereIn('category', CategoryEnum::cases());如果传入请求与路由模式约束不匹配,将返回 404 HTTP 响应。
全局约束
如果您希望路由参数始终受给定正则表达式约束,可以使用 pattern 方法。您应该在应用程序的 App\Providers\AppServiceProvider 类的 boot 方法中定义这些模式:
use Illuminate\Support\Facades\Route;
/**
* 引导任何应用程序服务。
*/
public function boot(): void
{
Route::pattern('id', '[0-9]+');
}一旦定义了模式,它会自动应用于使用该参数名称的所有路由:
Route::get('/user/{id}', function (string $id) {
// 仅在 {id} 为数字时执行...
});编码的正斜杠
Laravel 路由组件允许除 / 之外的所有字符出现在路由参数值中。您必须使用 where 条件正则表达式显式允许 / 成为占位符的一部分:
Route::get('/search/{search}', function (string $search) {
return $search;
})->where('search', '.*');WARNING
编码的正斜杠仅在最后一个路由段中受支持。
命名路由
命名路由允许方便地为特定路由生成 URL 或重定向。您可以通过将 name 方法链接到路由定义来为路由指定名称:
Route::get('/user/profile', function () {
// ...
})->name('profile');您也可以为控制器操作指定路由名称:
Route::get(
'/user/profile',
[UserProfileController::class, 'show']
)->name('profile');WARNING
路由名称应始终唯一。
为命名路由生成 URL
一旦为给定路由分配了名称,您可以在通过 Laravel 的 route 和 redirect 辅助函数生成 URL 或重定向时使用路由名称:
// 生成 URL...
$url = route('profile');
// 生成重定向...
return redirect()->route('profile');
return to_route('profile');如果命名路由定义了参数,您可以将参数作为第二个参数传递给 route 函数。给定的参数将自动插入到生成 URL 的正确位置:
Route::get('/user/{id}/profile', function (string $id) {
// ...
})->name('profile');
$url = route('profile', ['id' => 1]);如果您在数组中传递额外的参数,这些键/值对将自动添加到生成 URL 的查询字符串中:
Route::get('/user/{id}/profile', function (string $id) {
// ...
})->name('profile');
$url = route('profile', ['id' => 1, 'photos' => 'yes']);
// http://example.com/user/1/profile?photos=yesNOTE
有时,您可能希望为 URL 参数指定请求范围的默认值,例如当前区域设置。为此,您可以使用 URL::defaults 方法。
检查当前路由
如果您想确定当前请求是否路由到给定的命名路由,可以在 Route 实例上使用 named 方法。例如,您可以从路由中间件检查当前路由名称:
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* 处理传入请求。
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->route()->named('profile')) {
// ...
}
return $next($request);
}路由组
路由组允许您在大量路由之间共享路由属性(如中间件),而无需在每个单独的路由上定义这些属性。
嵌套组尝试智能地与父组"合并"属性。中间件和 where 条件被合并,而名称和前缀被附加。命名空间分隔符和 URI 前缀中的斜杠会在适当的地方自动添加。
中间件
要向组内的所有路由分配中间件,可以在定义组之前使用 middleware 方法。中间件按它们在数组中列出的顺序执行:
Route::middleware(['first', 'second'])->group(function () {
Route::get('/', function () {
// 使用 first 和 second 中间件...
});
Route::get('/user/profile', function () {
// 使用 first 和 second 中间件...
});
});控制器
如果一组路由都使用相同的控制器,可以使用 controller 方法为组内的所有路由定义公共控制器。然后,在定义路由时,您只需要提供它们调用的控制器方法:
use App\Http\Controllers\OrderController;
Route::controller(OrderController::class)->group(function () {
Route::get('/orders/{id}', 'show');
Route::post('/orders', 'store');
});子域名路由
路由组也可用于处理子域名路由。子域名可以像路由 URI 一样分配路由参数,允许您捕获子域名的一部分以在路由或控制器中使用。可以通过在定义组之前调用 domain 方法来指定子域名:
Route::domain('{account}.example.com')->group(function () {
Route::get('/user/{id}', function (string $account, string $id) {
// ...
});
});路由前缀
prefix 方法可用于为组内的每个路由添加给定的 URI 前缀。例如,您可能希望为组内的所有路由 URI 添加 admin 前缀:
Route::prefix('admin')->group(function () {
Route::get('/users', function () {
// 匹配 "/admin/users" URL
});
});路由名称前缀
name 方法可用于为组内的每个路由名称添加给定的字符串前缀。例如,您可能希望为组内所有路由的名称添加 admin 前缀。给定的字符串完全按照指定的方式添加到路由名称前,因此我们将确保在前缀中提供尾随的 . 字符:
Route::name('admin.')->group(function () {