路由
基本路由
最基本的 Laravel 路由接收一个 URI 和一个闭包,提供了非常简单且直观的方式来定义路由:
Route::get('foo', function () {
return 'Hello World';
});
默认路由文件
所有的 Laravel 路由都在 routes
目录中定义,这些文件由框架自动加载。routes/web.php
文件用于定义 Web 界面的路由。这些路由被分配给 web
中间件组,它提供了会话状态和 CSRF 保护等功能。routes/api.php
中的路由都是无状态的,并被分配给 api
中间件组。
对于大多数应用,您都会先在 routes/web.php
文件中定义路由。routes/web.php
中定义的路由可以通过在浏览器中输入路由中定义的 URL 来访问。例如,您可以通过在浏览器中导航到 http://your-app.test/user
来访问以下路由:
Route::get('/user', 'UserController@index');
在 routes/api.php
文件中定义的路由通过 RouteServiceProvider
被嵌套到一个路由组里。该路由组中的路由会自动添加 URL 前缀 /api
,这样您就不用手动为每个路由添加前缀了。您可以在 RouteServiceProvider
类中修改此前缀和其它路由组选项。
可用的路由器方法
路由器允许您注册响应任何 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('foo', function () {
//
});
CSRF 保护
任何指向 web
路由文件中定义的 POST
、PUT
或者 DELETE
路由的 HTML 表单,都应该包含一个 CSRF 令牌字段。否则,该请求将被拒绝。您可以在 CSRF 文档 中阅读有关 CSRF 保护的更多内容:
<form method="POST" action="/profile">
@csrf
...
</form>
重定向路由
如果要定义一个重定向到另一个 URI 的路由,可以使用 Route::redirect
方法。该方法可以方便地实现重定向,而无需定义完整的路由或者控制器:
Route::redirect('/here', '/there', 301);
视图路由
如果路由只要返回视图,可以使用 Route::view
方法。与 redirect
方法一样,该方法提供了一个简便的方式,因此无需定义完整的路由或控制器。view
方法有三个参数,其中前两个是必填参数,分别是 URI 和视图名称。第三个参数选填,可以传入一个数组,数组中的数据会被传递给视图:
Route::view('/welcome', 'welcome');
Route::view('/welcome', 'welcome', ['name' => 'Taylor']);
路由参数
必填参数
当然,有时需要获取路由中的 URI 字段。例如,从 URI 中获取用户的 ID。可以通过定义路由参数来这样做:
Route::get('user/{id}', function ($id) {
return 'User '.$id;
});
也可以根据需要在路由中定义多个参数:
Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
//
});
路由参数放在 {}
中,并且应由字母组成,同时不能包含 -
。如需使用 -
,可以使用下划线(_
)代替。路由参数会按顺序依次注入到路由回调/控制器中——而不受回调/控制器参数名的影响。
可选参数
有时可能需要指定一个路由参数,但希望该参数是可选的。那么可以在参数后面加上 ?
。确保给对应变量一个默认值:
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
return $name;
});
正则表达式约束
可以使用路由实例的 where
方法约束路由参数的格式。where
方法接收参数名和定义应如何约束参数的正则表达式:
Route::get('user/{name}', function ($name) {
//
})->where('name', '[A-Za-z]+');
Route::get('user/{id}', function ($id) {
//
})->where('id', '[0-9]+');
Route::get('user/{id}/{name}', function ($id, $name) {
//
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
全局约束
如果您希望某个路由参数始终都受同一正则表达式的约束,可以使用 pattern
方法。应该在 RouteServiceProvider
的 boot
方法中定义这些约束:
/**
* 定义路由绑定,约束条件等等
*
* @return void
*/
public function boot()
{
Route::pattern('id', '[0-9]+');
parent::boot();
}
定义好约束之后,会自动应用到使用该参数名的所有路由:
Route::get('user/{id}', function ($id) {
// 当 {id} 是数字时才会执行
});
命名路由
对路由命名后,可以方便地生成 URL 或者重定向。通过在路由上链式调用 name
方法来指定路由名称:
Route::get('user/profile', function () {
//
})->name('profile');
还可以为控制器操作指定路由名称:
Route::get('user/profile', 'UserProfileController@show')->name('profile');
为命名路由生成 URL
为给定路由指定名称后,就可以使用路由名称来生成 URL,或者通过全局辅助函数 route
生成重定向:
// 生成 URL
$url = route('profile');
// 生成重定向
return redirect()->route('profile');
如果命名路由定义了参数,可以将参数作为第二个参数传递 route
函数。给定的参数将自动插入到 URL 中正确的位置:
Route::get('user/{id}/profile', function ($id) {
//
})->name('profile');
$url = route('profile', ['id' => 1]);
检查当前路由
如果要判断当前请求是否路由到了给定的命名路由,可以在路由实例上调用 named
方法。例如,可以在路由中间件中检查当前路由名称:
/**
* 处理传入的请求
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->route()->named('profile')) {
//
}
return $next($request);
}
路由组
路由组允许您在大量路由之间共享路由属性(例如中间件或命名空间),而不需要为每个路由单独定义这些属性。共享属性应以数组的形式传到 Route::group
方法的第一个参数。
中间件
要将中间件分配给路由组中所有的路由,可以在定义路由组之前调用 middleware
方法。中间件会按它们在数组中的顺序依次执行:
Route::middleware(['first', 'second'])->group(function () {
Route::get('/', function () {
// 使用 first 和 second 中间件
});
Route::get('user/profile', function () {
// 使用 first 和 second 中间件
});
});
命名空间
另一个路由组的常见用例是使用 namespace
方法将相同 PHP 命名空间分配给路由组的中所有的控制器:
Route::namespace('Admin')->group(function () {
// 控制器都在「App\Http\Controllers\Admin」命名空间下
});
要记住的是,默认情况下,RouteServiceProvider
会在命名空间组中引入路由文件,使您不用指定完整的 App\Http\Controllers
命名空间前缀就能注册控制器路由。因此,只需要指定命名空间 App\Http\Controllers
之后的部分即可。
子域名路由
路由组也可以用于处理子域名路由。子域名可以像路由 URI 一样分配路由参数,允许您获取子域名的参数部分给路由或控制器使用。可以在定义路由组之前调用 domain
方法来指定子域名:
Route::domain('{account}.myapp.com')->group(function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
路由前缀
prefix
方法可用于为路由组中所有路由的 URI 添加给定的前缀。例如,您可以为路由组中所有路由的 URI 加上 admin
前缀:
Route::prefix('admin')->group(function () {
Route::get('users', function () {
// 匹配「/admin/users」URL
});
});
路由名称前缀
name
方法可用于为路由组中所有路由名称添加给定的前缀。例如,您可以为路由组中所有路由名称加上 admin
前缀。由于给定字符串添加前缀后和路由名称完全一致,因此我们要确保在前缀最后添加一个 .
字符:
Route::name('admin.')->group(function () {
Route::get('users', function () {
// 路由名是「admin.users」
})->name('users');
});
路由模型绑定
将模型 ID 注入路由或控制器操作时,通常会查询该 ID 对应的模型。Laravel 路由模型绑定提供了直接将模型实例自动注入到路由的便捷方法。例如,您可以注入和给定用户 ID 匹配的 User
模型,而不是用户 ID。
隐式绑定
如果路由或控制器操作中类型提示的变量名与路由的参数名相匹配,那么 Laravel 会自动解析对应的 Eloquent 模型。例如:
Route::get('api/users/{user}', function (App\User $user) {
return $user->email;
});
由于 $user
变量被类型提示为 Eloquent 模型 App\User
,变量名又与 URI 中的 {user}
相匹配,因此 Laravel 会自动注入与所请求 URI 中传入的 ID 匹配的用户模型实例。如果在数据库中找不到对应的模型实例,则会自动生成一个 404 HTTP 响应。
自定义键名
如果您希望模型绑定时使用除 id
之外的字段来查询给定的模型类,可以重写 Eloquent 模型的 getRouteKeyName
方法:
/**
* 获取模型的路由键名
*
* @return string
*/
public function getRouteKeyName()
{
return 'slug';
}
显式绑定
要注册显式绑定,可以使用路由器的 model
方法来为给定参数指定类。您应该在 RouteServiceProvider
类的 boot
方法中定义显式模型绑定:
public function boot()
{
parent::boot();
Route::model('user', App\User::class);
}
接下来,定义一个包含 {user}
参数的路由:
Route::get('profile/{user}', function (App\User $user) {
//
});
由于我们已将所有 {user}
参数绑定到 App\User
模型,因此 User
实例将被注入到该路由。例如,profile/1
请求会注入数据库中 ID 为 1 的 User
实例。
如果在数据库中找不到对应的模型实例,则会自动生成一个 404 HTTP 响应。
自定义解析逻辑
如果您希望使用自己的解析逻辑,可以使用 Route::bind
方法。传递给 bind
方法的闭包接收 URI 字段的值,并返回要注入到该路由的类的实例:
public function boot()
{
parent::boot();
Route::bind('user', function ($value) {
return App\User::where('name', $value)->first() ?? abort(404);
});
}
回退路由
使用 Route::fallback
方法,可以定义请求在没有匹配到任何路由时执行的路由。通常来说,未处理的请求会由应用的异常处理器渲染为「404」页面。然而,由于您可以在 routes/web.php
文件中定义 fallback
路由,因此所有 web
中间件组中的中间件都会应用到该路由。当然,您可以根据需要在路由上添加其它中间件:
Route::fallback(function () {
//
});
速率限制
Laravel 包含一个 中间件 用于在应用中限制路由访问速率。首先,将 throttle
中间件分配给一个路由或路由组。throttle
中间件接收两个参数,决定了在给定的分钟数内最多可以请求多少次。例如,我们指定一个认证用户每分钟最多可以访问以下路由组 60 次:
Route::middleware('auth:api', 'throttle:60,1')->group(function () {
Route::get('/user', function () {
//
});
});
动态速率限制
您可以根据认证用户 User
模型的属性来动态请求最大值。例如,如果 User
模型包含 rate_limit
属性,则可以将该属性的名称传给 throttle
中间件,以便用来计算最大请求数:
Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () {
Route::get('/user', function () {
//
});
});
表单方法伪造
HTML 表单不支持 PUT
、PATCH
或 DELETE
方法。所以当在 HTML 表单中使用 PUT
、PATCH
或 DELETE
定义的路由时,您需要在表单中增加一个隐藏的 _method
字段。并将 HTTP 请求类型作为 _method
字段的值一起发送:
<form action="/foo/bar" method="POST">
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>
还可以使用 Blade 指令 @method
来生成 _method
表单隐藏域:
<form action="/foo/bar" method="POST">
@method('PUT')
@csrf
</form>
获取当前路由
您可以使用 Route
Facade 的 current
、currentRouteName
和 currentRouteAction
方法来获取当前请求的路由信息:
$route = Route::current();
$name = Route::currentRouteName();
$action = Route::currentRouteAction();
参阅 路由 Facade 的底层类 和 路由实例 的 API 文档了解更多可用方法。