Skip to content

路由

基本路由

最基本的 Laravel 路由接受一个 URI 和一个 Closure,提供了一种非常简单且富有表现力的方法来定义路由:

php
Route::get('foo', function () {
    return 'Hello World';
});

默认路由文件

所有的 Laravel 路由都定义在你的路由文件中,这些文件位于 routes 目录中。这些文件会被框架自动加载。routes/web.php 文件定义了用于你的 Web 界面的路由。这些路由被分配到 web 中间件组中,提供了会话状态和 CSRF 保护等功能。routes/api.php 中的路由是无状态的,并被分配到 api 中间件组中。

对于大多数应用程序,你将从在 routes/web.php 文件中定义路由开始。可以通过在浏览器中输入定义的路由 URL 来访问 routes/web.php 中定义的路由。例如,你可以通过在浏览器中导航到 http://your-app.dev/user 来访问以下路由:

php
Route::get('/user', 'UserController@index');

routes/api.php 文件中定义的路由被 RouteServiceProvider 嵌套在一个路由组中。在这个组中,/api URI 前缀会被自动应用,因此你不需要手动将其应用到文件中的每个路由。你可以通过修改 RouteServiceProvider 类来修改前缀和其他路由组选项。

可用的路由器方法

路由器允许你注册响应任何 HTTP 动词的路由:

php
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 动词的路由:

php
Route::match(['get', 'post'], '/', function () {
    //
});

Route::any('foo', function () {
    //
});

CSRF 保护

任何指向 POSTPUTDELETE 路由的 HTML 表单都应该在 web 路由文件中包含一个 CSRF 令牌字段。否则,请求将被拒绝。你可以在 CSRF 文档 中阅读更多关于 CSRF 保护的信息:

php
<form method="POST" action="/profile">
    {{ csrf_field() }}
    ...
</form>

重定向路由

如果你正在定义一个重定向到另一个 URI 的路由,你可以使用 Route::redirect 方法。此方法提供了一个方便的快捷方式,因此你不必为执行简单的重定向定义完整的路由或控制器:

php
Route::redirect('/here', '/there', 301);

视图路由

如果你的路由只需要返回一个视图,你可以使用 Route::view 方法。与 redirect 方法类似,此方法提供了一个简单的快捷方式,因此你不必定义完整的路由或控制器。view 方法接受一个 URI 作为第一个参数和一个视图名称作为第二个参数。此外,你可以提供一个数组作为可选的第三个参数来传递给视图的数据:

php
Route::view('/welcome', 'welcome');

Route::view('/welcome', 'welcome', ['name' => 'Taylor']);

路由参数

必需参数

当然,有时你需要在路由中捕获 URI 的片段。例如,你可能需要从 URL 中捕获用户的 ID。你可以通过定义路由参数来实现:

php
Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});

你可以根据路由的需要定义任意数量的路由参数:

php
Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //
});

路由参数总是用 {} 括起来,并且应该由字母字符组成,不能包含 - 字符。请使用下划线 (_) 代替 - 字符。路由参数会根据它们的顺序注入到路由回调/控制器中 - 回调/控制器参数的名称无关紧要。

可选参数

有时你可能需要指定一个路由参数,但使该路由参数的存在是可选的。你可以通过在参数名称后加上 ? 来实现。确保给路由的对应变量一个默认值:

php
Route::get('user/{name?}', function ($name = null) {
    return $name;
});

Route::get('user/{name?}', function ($name = 'John') {
    return $name;
});

正则表达式约束

你可以使用路由实例上的 where 方法来约束路由参数的格式。where 方法接受参数的名称和一个定义参数应如何约束的正则表达式:

php
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 方法。你应该在 RouteServiceProviderboot 方法中定义这些模式:

php
/**
 * 定义你的路由模型绑定、模式过滤器等。
 *
 * @return void
 */
public function boot()
{
    Route::pattern('id', '[0-9]+');

    parent::boot();
}

一旦定义了模式,它会自动应用于所有使用该参数名称的路由:

php
Route::get('user/{id}', function ($id) {
    // 仅在 {id} 为数字时执行...
});

命名路由

命名路由允许为特定路由方便地生成 URL 或重定向。你可以通过在路由定义上链接 name 方法来为路由指定名称:

php
Route::get('user/profile', function () {
    //
})->name('profile');

你还可以为控制器动作指定路由名称:

php
Route::get('user/profile', 'UserController@showProfile')->name('profile');

生成命名路由的 URL

一旦你为给定路由分配了名称,你可以在通过全局 route 函数生成 URL 或重定向时使用路由的名称:

php
// 生成 URL...
$url = route('profile');

// 生成重定向...
return redirect()->route('profile');

如果命名路由定义了参数,你可以将参数作为 route 函数的第二个参数传递。给定的参数将自动插入到 URL 中的正确位置:

php
Route::get('user/{id}/profile', function ($id) {
    //
})->name('profile');

$url = route('profile', ['id' => 1]);

检查当前路由

如果你想确定当前请求是否路由到给定的命名路由,你可以在 Route 实例上使用 named 方法。例如,你可以从路由中间件中检查当前路由名称:

php
/**
 * 处理传入的请求。
 *
 * @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 方法。中间件按它们在数组中的顺序执行:

php
Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // 使用 first 和 second 中间件
    });

    Route::get('user/profile', function () {
        // 使用 first 和 second 中间件
    });
});

命名空间

路由组的另一个常见用例是使用 namespace 方法为一组控制器分配相同的 PHP 命名空间:

php
Route::namespace('Admin')->group(function () {
    // 控制器在 "App\Http\Controllers\Admin" 命名空间内
});

请记住,默认情况下,RouteServiceProvider 在一个命名空间组中包含你的路由文件,允许你在注册控制器路由时不指定完整的 App\Http\Controllers 命名空间前缀。因此,你只需指定在基本 App\Http\Controllers 命名空间之后的部分。

子域路由

路由组也可以用于处理子域路由。子域可以像路由 URI 一样分配路由参数,允许你在路由或控制器中使用子域的一部分。可以通过在定义组之前调用 domain 方法来指定子域:

php
Route::domain('{account}.myapp.com')->group(function () {
    Route::get('user/{id}', function ($account, $id) {
        //
    });
});

路由前缀

prefix 方法可用于为组中的每个路由添加给定的 URI 前缀。例如,你可能希望为组内的所有路由 URI 添加 admin 前缀:

php
Route::prefix('admin')->group(function () {
    Route::get('users', function () {
        // 匹配 "/admin/users" URL
    });
});

路由名称前缀

name 方法可用于为组中的每个路由名称添加给定的字符串前缀。例如,你可能希望为组内的所有路由名称添加 admin 前缀。给定的字符串会按指定的方式添加到路由名称中,因此我们会确保在前缀中提供尾随的 . 字符:

php
Route::name('admin.')->group(function () {
    Route::get('users', function () {
        // 路由分配名称 "admin.users"...
    })->name('users');
});

路由模型绑定

在将模型 ID 注入到路由或控制器动作时,你通常会查询以检索与该 ID 对应的模型。Laravel 路由模型绑定提供了一种方便的方法,可以将模型实例直接注入到你的路由中。例如,代替注入用户的 ID,你可以注入与给定 ID 匹配的整个 User 模型实例。

隐式绑定

Laravel 自动解析在路由或控制器动作中定义的 Eloquent 模型,其类型提示的变量名称与路由段名称匹配。例如:

php
Route::get('api/users/{user}', function (App\User $user) {
    return $user->email;
});

由于 $user 变量被类型提示为 App\User Eloquent 模型,并且变量名称与 {user} URI 段匹配,Laravel 将自动注入与请求 URI 中对应值匹配的模型实例。如果在数据库中找不到匹配的模型实例,将自动生成 404 HTTP 响应。

自定义键名

如果你希望模型绑定在检索给定模型类时使用 id 以外的数据库列,你可以在 Eloquent 模型上重写 getRouteKeyName 方法:

php
/**
 * 获取模型的路由键。
 *
 * @return string
 */
public function getRouteKeyName()
{
    return 'slug';
}

显式绑定

要注册显式绑定,请使用路由器的 model 方法为给定参数指定类。你应该在 RouteServiceProvider 类的 boot 方法中定义你的显式模型绑定:

php
public function boot()
{
    parent::boot();

    Route::model('user', App\User::class);
}

接下来,定义一个包含 {user} 参数的路由:

php
Route::get('profile/{user}', function ($user) {
    //
});

由于我们已将所有 {user} 参数绑定到 App\User 模型,因此将注入一个 User 实例到路由中。因此,例如,请求 profile/1 将从数据库中注入 ID 为 1User 实例。

如果在数据库中找不到匹配的模型实例,将自动生成 404 HTTP 响应。

自定义解析逻辑

如果你希望使用自己的解析逻辑,可以使用 Route::bind 方法。传递给 bind 方法的 Closure 将接收 URI 段的值,并应返回应注入到路由中的类的实例:

php
public function boot()
{
    parent::boot();

    Route::bind('user', function ($value) {
        return App\User::where('name', $value)->first() ?? abort(404);
    });
}

表单方法欺骗

HTML 表单不支持 PUTPATCHDELETE 操作。因此,当定义从 HTML 表单调用的 PUTPATCHDELETE 路由时,你需要在表单中添加一个隐藏的 _method 字段。与 _method 字段一起发送的值将用作 HTTP 请求方法:

php
<form action="/foo/bar" method="POST">
    <input type="hidden" name="_method" value="PUT">
    <input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>

你可以使用 method_field 助手生成 _method 输入:

php
{{ method_field('PUT') }}

访问当前路由

你可以使用 Route facade 上的 currentcurrentRouteNamecurrentRouteAction 方法来访问处理传入请求的路由的信息:

php
$route = Route::current();

$name = Route::currentRouteName();

$action = Route::currentRouteAction();

请参阅 Route facade 的底层类Route 实例 的 API 文档,以查看所有可访问的方法。