laravel 学习指南 第五章 第一节
- TOC
 {:toc}
5 基本功能
5.1 路由
5.1.1 基本路由
laravel所有的路由都可以定义在App/Http/routes.php文件,它会被App/Providers/RouteServiceProvider类载入。
下方是一个最基本的get与post。get接收URL,post为一个闭包。
| 1 | Route::get('foo', function () { | 
默认情况下,routes.php文件包含单个路由和一个套用web中间件的路由组。
这个中间件路由组提供了session状态以及CSRF保护。
一般情况下,会将全部路由放置在这个路由组。
5.1.1.1 有效的路由方法
注册路由来响应任何HTTP请求:
下方是HTTP请求方法:
| 1 | Route::get($uri, $callback); | 
有时候还需要注册路由响应多个HTTP请求,这时可以使用match方法来实现。甚至可以使用any方法注册一个路由来响应所有HTTP请求:
| 1 | Route::match(['get', 'post'], '/', function () { | 
当路由过多时,使用
控制器是个好方法。
5.1.2 路由参数
5.1.2.1 基础路由参数
有时可能需要在路由中捕获URL片段。比如,要从URL中捕获用户ID,需要通过下方代码定义路由参数:
| 1 | Route::get('user/{id}', function ($id) { | 
可以按照需要在路由中定义多个路由参数:
| 1 | Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) { | 
路由参数总是通过{ }进行包裹,这些参数在路由被执行时会被传递到路由的闭包。
注意:路由参数不能包含-,要使用_替换。
5.1.2.2 选择性路由参数
有时候可能需要指定可选的路由参数,这可以通过在参数后加?标记来实现,这种情况下需要指定变量的默认值:
下方代码先设定name可以为空,再设定默认name为John。 
| 1 | Route::get('user/{name?}', function ($name = null) { | 
5.1.2.3 正则约束
路由设置中可以使用where方法来约束路由参数。where方法接收参数名和一个正则表达式来定义参数如何被约束:
| 1 | Route::get('user/{name}', function ($name) { | 
5.1.2.4 全局约束
如果想要全局约束,可以使用pattern方法。在RouteServiceProvider类的boot方式中定义约束:
| 1 | /** | 
一旦模式被定义,将会自动应用到所有包含参数名的路由中:
| 1 | Route::get('user/{id}', function ($id) { | 
上方例子中限定id只能是数字。
5.1.3 命令路由
命令路由为生成URL或重新定向提供方便。在定义路由是使用as指定路由名称:
| 1 | Route::get('user/profile', ['as' => 'profile', function () { | 
此外,还可以为控制器动作指定路由名称:
| 1 | Route::get('user/profile', [ | 
还有另一种,路由命名的方法,比如使用name方式来实现:
| 1 | Route::get('user/profile',function () { | 

5.1.3.1 路由群组 & 命名路由
如果你在使用路由群组,可以通过在路由群组的属性数组中指定as关键字来为群组的路由设置一个共用的路由名前缀:
| 1 | Route::group(['as' => 'admin::'], function () { | 

5.1.3.2 为命名路由生成URL
如果为给定路由进行了命名,就可以通过全局route函数为该命名路由生成对应URL,或者重新定向:
| 1 | $url = route('profile'); //产生URL | 
如果命名路由定义了参数,可以将该参数作为第二个参数穿传递给route函数。路由参数会自动插入URL中。
| 1 | Route::get('user/{id}/profile', ['as' => 'profile', function ($id) { | 
5.1.4 路由群组
路由组允许共享路由属性,比如中间件与命名空间等。这样利用路由群组套用这些属性到多个路由,而不需要在每个路由都设定一次。
共享属性被要求为数组格式。共享属性作为第一个参数被传递给Route::group方法。
5.1.4.1 中间件
要给路由组中所有路由分配中间件,可以在群组属性中使用middleware。中间件将会按照数组中定义的顺序依次执行:
| 1 | Route::group(['middleware' => 'auth'], function () { | 
5.1.4.2 命名空间
另一个例子,指定相同的PHP命名空间下多个控制器,可以在分组属性数组中使用namespace来指定群组中所有控制器的公共命名空间:
| 1 | Route::group(['namespace' => 'Admin'], function(){ | 
默认情况下,RouteServiceProvider会在命名空间群组内导入routes.php文件,让你不用指定完整App/Http/Controllers命令空间,就能注册控制器。所有,只需要指定在App/Http/Controllers之后的命名。
5.1.4.3 子域名路由
子域名可以像URL一样被分配给路由参数,从而允许捕获子域名的部分用于路由或者控制器,子域名可以通过群组属性中的domani来指定:
| 1 | Route::group(['domain' => '{account}.myapp.com'], function () { | 
注意:其中的{account}可以随意修改的。{id}也是可以随意更改的。
5.1.4.4 路由前缀
prefix群组属性,用来为群组中每个路由添加一个给定URL前缀。
例如,你可以为所有路由URL添加admin前缀:
| 1 | Route::group(['prefix' => 'admin'], function () { | 
还可以使用prefix参数为路由组指定公共路由参数:
| 1 | Route::group(['prefix' => 'accounts/{account_id}'], function () { | 
5.1.5 CSRF保护
5.1.5.1 简介
CSRF指跨网站请求伪造。跨网站请求伪伪造是一种恶意攻击,透过经身份验证的使用者身份执行未经授权的命令。
laravel会自动产生一个CSRF令牌给每个被应用管理的有效的用户,该令牌用于验证授权用户和发起请求者是否为同一个人。
想要生成包含CSRF令牌的隐藏输入字段,可以使用csrf_field函数来实现:
| 1 | echo csrf_field(); | 
csrf_field函数会生成如下HTML:
| 1 | <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> | 
当然推荐使用Blade引擎模板提供的方式:
| 1 | {!! csrf_field() !!} | 
不需要自己编写代码去验证POST、PUT或者DELETE请求的CSRF令牌,因为Laravel自带的HTTP中间件VerfyCsrfToken会完成这些工作。
只要将请求中的输入token和Session中存储的token作对比来进行验证。
5.1.5.2 不受 CSRF 保护的 URIs
有时需要从CSRF保护中排除一些URL。比如,如果使用Stripe来处理支付并用到他们的webhook系统,这时就需要从laravel的CSRF保护中排除webhook处理路由。
要实现这一目的,需要在VerifyCsrfToken中间件中将要排除的URL添加到$except属性:
| 1 | 
 | 
5.1.5.3 X-CSRF-Token
除了检查当前POST参数的CDSRF令牌外,在laravel的VerifyCsrfToken中介层也会确认请求头部中的X-CSRF-Token。
例如,可以将其存储在meta标签中:
| 1 | <meta name="csrf-token" content="{{ csrf_token() }}"> | 
一旦建立了meta标签,就可以使用jQuery之类的函数库,将令牌加入到所有请求头部。
这为基于AJAX的应用提供了简单、方便的方式来避免CSRF攻击。
| 1 | $.ajaxSetup({ | 
5.1.5.4 X-XSRF-Token
laravel还会将CSRF令牌保存到名为XSRF-TOKEN的Cookie中,可以使用该Cookie值来设置X-XSRF-TOKEN请求头。
一些javascript框架,比如Angular,会为你自动进行设置,基本上不太需要手动设置这个值。
5.1.6 路由模型绑定
laravel路由模型绑定提供了一个方便的方式来注入类至路由中。
例如,可以将匹配到ID的整个User类注入到路由中,而不是直接注入用户ID。
5.1.6.1 隐式绑定
laravel会自动解析定义在路由或控制器动作(变量名匹配路由片段)中的Eloquent模型类型声明,例如:
| 1 | Route::get('api/users/{user}', function (App\User $user) { | 
这个例子中,路由URL的user符合$user实例的ELoquent模型,所以laravel会自动注入与请求URL的ID对应的模型实例。
如果找不到对应的模型实例,会自动生成HTTP404响应。
- 自定义键名 - 如果想要隐式模型绑定,使用数据表的其他字段,可以重写Eloquent模型类的 - getRouteKeyName方法:- 1 
 2
 3
 4
 5
 6
 7
 8
 9- /** 
 * Get the route key for the model.
 *
 * @return string
 */
 public function getRouteKeyName()
 {
 return 'slug';
 }- 现在就可以使用 - slug作为键值了,可以用来- 模糊关键字。
5.1.6.2 显式绑定
要注册显式绑定,需要使用路由的model方法来为给定参数指定绑定类。
必须在RouteServiceProvider::boot方法中定义模型绑定。
- 绑定参数到模型 - 1 
 2
 3
 4
 5- public function boot(Router $router) 
 {
 parent::boot($router);
 $router->model('user', 'App\User');
 }- 接下来,定义一个包含 - user参数的路由:- 1 
 2
 3- $router->get('profile/{user}', function(App\User $user) { 
 //
 });- 由于已经绑定 - {user}参数到- App\User模型,User实例会被注入到该路由。因此,如果请求URL是- profile/1,就会注入一个用户ID为1的User实例。- 如果匹配的模型实例在数据库不存在,会自动生成并返回HTTP404响应。 
- 自定义解析逻辑 - 如果想要使用自定义的解析逻辑需要使用 - Route::bind方法,传递到- bind方法的闭关会获取到URL请求的参数中的值,并返回你想要在该路由中注入的类实例:- 1 
 2
 3- $router->bind('user', function($value) { 
 return App\User::where('name', $value)->first();
 });
- 自定义”Not Found” - 如果想要指定自己的 - Not Found行为,将封装该行为的闭包作为第三个参数传递给- model方法- 1 
 2
 3- $router->model('user', 'App\User', function() { 
 throw new NotFoundHttpException;
 });
5.1.7 表单方法伪造
HTML表单不支持PUT、PATCH或者DELETE请求方法。当使用这些路由时,需要添加一个隐藏的_method字段到表单中,其值被用作该表单的HTTP请求方法:
| 1 | <form action="/foo/bar" method="POST"> | 
还可以使用辅助函数method_field来实现这一目的:
| 1 | echo method_field('PUT'); | 
当然,也支持Blade模板引擎:
{method——field('PUT')}