1、基本路由
最基本的 Laravel
路由只接收一个 URI
和一个闭包
,并以此提供一个非常简单且优雅的定义路由方法:
Route::get('foo', function () {
return 'Hello World';
});
默认路由文件
有 Laravel 路由都定义在位于 routes
目录下的路由文件中,这些文件通过框架自动加载。
routes/web.php
文件定义了web界面的路由,这些路由被分配了web
中间件组,从而可以提供session和csrf防护等功能。
routes/api.php
中的路由是无状态的,被分配了 api
中间件组。
对大多数应用而言,都是从 routes/web.php
文件开始定义路由。
我们可以注册路由来响应任何 HTTP 请求:
//get 请求
Route::get($uri, $callback);
//post 请求
Route::post($uri, $callback);
//put 请求
Route::put($uri, $callback);
//patch 请求
Route::patch($uri, $callback);
//delete 请求
Route::delete($uri, $callback);
//options 请求
Route::options($uri, $callback);
有时候还需要注册路由响应多个 HTTP
请求——这可以通过 match
方法来实现。或者,可以使用 any
方法注册一个路由来响应所有 HTTP 请求:
Route::match(['get', 'post'], '/', function () {
//
});
Route::any('foo', function () {
//
});
CSRF防护
在 web
路由文件中所有请求方式为PUT
、POST
或DELETE
的HTML表单都会包含一个CSRF令牌
字段,否则,请求会被拒绝。关于CSRF的更多细节,可以参考CSRF文档:
2、路由参数
必选参数
有时我们需要在路由中捕获 URI 片段。比如,要从 URL
中捕获用户ID
,需要通过如下方式定义路由参数:
Route::get('user/{id}', function ($id) {
return 'User '.$id;
});
可以按需要在路由中定义多个路由参数:
Route::get('/hello/{name}/by/{user}',function($name,$user){
return "Hello {$name} by {$user}!";
});
路由参数总是通过{}
进行包裹,这些参数在路由被执行时会被传递到路由的闭包。路由参数不能包含 -
字符,需要的话可以使用 _
替代。
注意以上参数是必选的,如果没有输入参数会抛出 MethodNotAllowedHttpException
或 NotFoundHttpException
异常。
此外闭包函数中的参数与路由参数一一对应。
可选参数
有时候可能需要指定可选的路由参数,这可以通过在参数名后加一个 ?
标记来实现,这种情况下需要给相应的变量指定默认值:
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
方法中定义约束模式:
/**
* 定义路由模型绑定,模式过滤器等
*
* @param \Illuminate\Routing\Router $router
* @return void
* @translator
*/
public function boot(){
Route::pattern('id', '[0-9]+');
parent::boot();
}
一旦模式被定义,将会自动应用到所有包含该参数名的路由中:
Route::get('user/{id}', function ($id) {
// 只有当 {id} 是数字时才会被调用
});
3、命名路由
命名路由为生成 URL
或重定向
提供了便利。实现也很简单,在路由定义之后使用 name
方法链的方式来实现:
Route::get('user/profile', function () {
//
})->name('profile');
还可以为控制器动作指定路由名称:
Route::get('user/profile', 'UserController@showProfile')->name('profile');
为命名路由生成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]);
4、路由群组
路由分组就是将一组拥有相同属性(中间件
、命名空间
、子域名
、路由前缀
等)的路由使用 Route Facade
的 group
方法聚合起来。
共享属性以数组的形式作为第一个参数被传递给 Route::group
方法。
中间件
要给路由群组中定义的所有路由分配中间件,可以在群组属性数组中使用 middleware
。中间件将会按照数组中定义的顺序依次执行:
Route::group(['middleware' => 'auth'], function () {
Route::get('/', function () {
// 使用 Auth 中间件
});
Route::get('user/profile', function () {
// 使用 Auth 中间件
});
});
命名空间
命名空间可以通过namespace
关键字来设置。
默认情况下, RouteServiceProvider
引入你的路由文件并指定其下所有控制器类所在的默认命名空间App\Http\Controllers
,因此,我们在定义的时候只需要指定命名空间 App\Http\Controllers
之后的部分即可 :
Route::group(['namespace' => 'Admin'], function(){
// 控制器在 "App\Http\Controllers\Admin" 命名空间下
});
子域名路由
子域名可以通过domain
关键字来设置 :
Route::group(['domain' => '{account}.myapp.com'], function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
这样我们在浏览器中访问http://xiao.myapp.com/user/5。
路由前缀
路由前缀可以通过 prefix 关键字来设置 :
Route::group(['prefix' => 'admin'], function () {
Route::get('users', function () {
// 匹配 "/admin/users" URL
});
});
5、路由模型绑定
注入模型ID到路由或控制器动作时,通常需要查询数据库才能获取相应的模型数据。Laravel 路由模型绑定让注入模型实例到路由变得简单,例如,你可以将匹配给定 ID 的整个 User 类实例注入到路由中,而不是直接注入用户ID。
隐式绑定
Laravel 会自动解析定义在路由或控制器动作(变量名匹配路由片段)中的 Eloquent
模型类型声明,例如:
Route::get('api/users/{user}', function (App\User $user) {
return $user->email;
});
在这个例子中,由于类型声明了 Eloquent
模型 App\User
,对应的变量名 $user
会匹配路由片段中的 {user}
,这样,Laravel 会自动注入与请求 URI 中传入的 ID 对应的用户模型实例。
如果数据库中找不到对应的模型实例,会自动生成 HTTP 404 响应。
自定义键名
如果你想要在隐式模型绑定中使用数据表的其它字段,可以重写 Eloquent 模型类的 getRouteKeyName 方法:
/**
* 获取模型的路由密钥.
*
* @return string
*/
public function getRouteKeyName()
{
return 'slug';
}
显式绑定
要注册显式绑定,需要使用路由的 model
方法来为给定参数指定绑定类。应该在 RouteServiceProvider
类的 boot
方法中定义模型绑定:
public function boot()
{
parent::boot();
Route::model('user', App\User::class);
}
接下来,定义一个包含 {user}
参数的路由:
$router->get('profile/{user}', function(App\User $user) {
//
});
由于我们已经绑定 {user}
参数到 App\User
模型,User
实例会被注入到该路由。因此,如果请求 URL 是 profile/1
,就会注入一个用户 ID 为 1
的 User
实例。
如果匹配的模型实例在数据库不存在,会自动生成并返回 HTTP 404 响应。
自定义解析逻辑
如果你想要使用自定义的解析逻辑,需要使用 Route::bind
方法,传递到 bind
方法的闭包会获取到 URI 请求参数中的值,并且返回你想要在该路由中注入的类实例:
public function boot()
{
parent::boot();
Route::bind('user', function($value) {
return App\User::where('name', $value)->first();
});
}
6、表单方法伪造
HTML 表单不支持 PUT
、PATCH
或者 DELETE
请求方法,因此,当定义 PUT
、PATCH
或 DELETE
路由时,需要添加一个隐藏的 _method
字段到表单中,其值被用作该表单的 HTTP 请求方法:
还可以使用辅助函数 method_field
来实现这一目的:
{{ method_field('PUT') }}
7、访问当前路由
你可以使用 Route Facade
上的 current
、currentRouteName
和 currentRouteAction
方法来访问处理当前输入请求的路由信息:
$route = Route::current();
$name = Route::currentRouteName();
$action = Route::currentRouteAction();
参考API文档了解路由门面底层类以及Route实例的更多可用方法。