[ Laravel 5.2 文档 ] 基础 —— HTTP 路由

正文开始

<p>http://laravelacademy.org/post/417.html</p><p>1、基本<a href="http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1" title="View all posts in 路由" target="_blank">路由</a></p><p>所有应用路由都定义在 AppProvidersRouteServiceProvider 类载入的 app/Http/routes.php 文件中。<br />最基本的 <a href="http://laravelacademy.org/tags/laravel" title="View all posts in Laravel" target="_blank">Laravel</a> 路由接收一个 URI 和一个闭包:</p><pre>Route::get('foo', function () { return 'Hello World'; }); Route::post('foo', function () { // });</pre><p>默认情况下,routes.php 文件包含单个路由和一个<a href="http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1%e7%be%a4%e7%bb%84" title="View all posts in 路由群组" target="_blank">路由群组</a>,该路由群组包含的所有路由都使用了<a href="http://laravelacademy.org/tags/%e4%b8%ad%e9%97%b4%e4%bb%b6" title="View all posts in 中间件" target="_blank">中间件</a>组 web,而这个中间件组为路由提供了 Session 状态和  <a href="http://laravelacademy.org/tags/csrf" title="View all posts in CSRF" target="_blank">CSRF</a> 保护功能。通常,我们会将所有路由定义在这个路由组中。</p>有效的<a href="http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1%e6%96%b9%e6%b3%95" title="View all posts in 路由方法" target="_blank">路由方法</a><p>我们可以注册路由来响应任何 <a href="http://laravelacademy.org/tags/http" title="View all posts in HTTP" target="_blank">HTTP</a> 请求:</p><pre>Route::get($uri, $callback); Route::post($uri, $callback); Route::put($uri, $callback); Route::patch($uri, $callback); Route::delete($uri, $callback); Route::options($uri, $callback);</pre><p>有时候还需要注册路由响应多个 HTTP 请求——这可以通过 match 方法来实现。或者,甚至可以使用 any 方法注册一个路由来响应所有 HTTP 请求:</p><pre>Route::match(['get', 'post'], '/', function () { // }); Route::any('foo', function () { // }); </pre>2、<a href="http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1%e5%8f%82%e6%95%b0" title="View all posts in 路由参数" target="_blank">路由参数</a>必选参数<p>有时我们需要在路由中捕获 URI 片段。比如,要从 <a href="http://laravelacademy.org/tags/url" title="View all posts in URL" target="_blank">URL</a> 中捕获用户 ID,需要通过如下方式定义路由参数:</p><pre>Route::get('user/{id}', function ($id) { return 'User '.$id; });</pre><p>可以按需要在路由中定义多个路由参数:</p><pre>Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) { // });</pre><p>路由参数总是通过花括号进行包裹,这些参数在路由被执行时会被传递到路由的闭包。</p><p>注意:路由参数不能包含 - 字符,需要的话可以使用 _ 替代。</p>可选参数<p>有时候可能需要指定可选的路由参数,这可以通过在参数名后加一个 ? 标记来实现,这种情况下需要给相应的变量指定默认值:</p><pre>Route::get('user/{name?}', function ($name = null) { return $name; }); Route::get('user/{name?}', function ($name = 'John') { return $name; });</pre>正则约束<p>可以使用路由实例上的where方法来约束路由参数的格式。where方法接收参数名和一个正则表达式来定义该参数如何被约束:</p><pre>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]+']);</pre>全局约束<p>如果想要路由参数在全局范围内被给定正则表达式约束,可以使用pattern方法。在RouteServiceProvider类的boot方法中定义约束模式:</p><pre>/** * 定义路由<a href="http://laravelacademy.org/tags/%e6%a8%a1%e5%9e%8b%e7%bb%91%e5%ae%9a" title="View all posts in 模型绑定" target="_blank">模型绑定</a>,模式过滤器等 * * @param IlluminateRoutingRouter $router * @return void * @translator http://laravelacademy.org */ public function boot(Router $router){ $router->pattern('id', '[0-9]+'); parent::boot($router); }</pre><p>一旦模式被定义,将会自动应用到所有包含该参数名的路由中:</p><pre>Route::get('user/{id}', function ($id) { // 只有当 {id} 是数字时才会被调用 });</pre>3、命名路由<p>命名路由为生成 URL 或重定向提供了便利。实现也很简单,在定义路由时使用数组键 as 指定路由名称:</p><pre>Route::get('user/profile', ['as' => 'profile', function () { // }]);</pre><p>此外,还可以为控制器动作指定路由名称:</p><pre>Route::get('user/profile', [ 'as' => 'profile', 'uses' => 'UserController@showProfile' ]);</pre><p>此外,除了在路由数组定义中指定路由名称外,还可以通过在路由定义之后使用 name 方法链的方式来实现:</p><pre>Route::get('user/profile', 'UserController@showProfile')->name('profile');</pre>路由群组 & 命名路由<p>如果你在使用路由群组,可以通过在路由群组的属性数组中指定 as 关键字来为群组中的路由设置一个共用的路由名前缀:</p><pre>Route::group(['as' => 'admin::'], function () { Route::get('dashboard', ['as' => 'dashboard', function () { // 路由被命名为 "admin::dashboard" }]); });</pre>为命名路由生成URL<p>如果你为给定路由进行了命名,就可以通过 route 函数为该命名路由生成对应 URL:</p><pre>$url = route('profile'); $redirect = redirect()->route('profile');</pre><p>如果命名路由定义了参数,可以将该参数作为第二个参数传递给 route 函数。给定的路由参数将会自动插入 URL 中:</p><pre>Route::get('user/{id}/profile', ['as' => 'profile', function ($id) { // }]); $url = route('profile', ['id' => 1]);</pre>4、路由群组<p>路由群组允许我们在多个路由中共享路由属性,比如中间件和<a href="http://laravelacademy.org/tags/%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4" title="View all posts in 命名空间" target="_blank">命名空间</a>等,这样的话我们就不必为每一个路由单独定义属性。共享属性以数组的形式作为第一个参数被传递给 Route::group 方法。</p><p>下面我们通过几个简单的应用实例来演示路由群组。</p>中间件<p>要给路由群组中定义的所有路由分配中间件,可以在群组属性数组中使用 middleware。中间件将会按照数组中定义的顺序依次执行:</p><pre>Route::group(['middleware' => 'auth'], function () { Route::get('/', function () { // 使用 Auth 中间件 }); Route::get('user/profile', function () { // 使用 Auth 中间件 }); });</pre>命名空间<p>另一个通用的例子是路由群组分配同一个 PHP 命名空间给其下的多个控制器,可以在分组属性数组中使用 namespace 来指定群组中所有控制器的公共命名空间:</p><pre>Route::group(['namespace' => 'Admin'], function(){ // 控制器在 "AppHttpControllersAdmin" 命名空间下 Route::group(['namespace' => 'User'], function(){ // 控制器在 "AppHttpControllersAdminUser" 命名空间下 }); });</pre><p>默认情况下,RouteServiceProvider 引入 routes.php 并指定其下所有控制器类所在的默认命名空间 AppHttpControllers,因此,我们在定义的时候只需要指定命名空间 AppHttpControllers 之后的部分即可。</p><a href="http://laravelacademy.org/tags/%e5%ad%90%e5%9f%9f%e5%90%8d" title="View all posts in 子域名" target="_blank">子域名</a>路由<p>路由群组还可以被用于子域名路由通配符,子域名可以像 URI 一样被分配给路由参数,从而允许捕获子域名的部分用于路由或者控制器,子域名可以通过群组属性数组中的 domain 来指定:</p><pre>Route::group(['domain' => '{account}.myapp.com'], function () { Route::get('user/{id}', function ($account, $id) { // }); });</pre>路由前缀<p>群组属性 prefix 可以用来为群组中每个路由添加一个给定 URI 前缀,比如,你可以为所有路由 URI 添加 admin 前缀 :</p><pre>Route::group(['prefix' => 'admin'], function () { Route::get('users', function () { // 匹配 "/admin/users" URL }); });</pre><p>你还可以使用 prefix 参数为路由群组指定公共路由参数:</p><pre>Route::group(['prefix' => 'accounts/{account_id}'], function () { Route::get('detail', function ($account_id) { // 匹配 accounts/{account_id}/detail URL }); });</pre>5、CSRF 攻击简介<p>跨站请求伪造是一种通过伪装授权用户的请求来利用授信网站的恶意漏洞。Laravel 使得防止应用遭到跨站请求伪造攻击变得简单。</p><p>Laravel 自动为每一个被应用管理的有效用户会话生成一个 CSRF “令牌”,该令牌用于验证授权用户和发起请求者是否是同一个人。想要生成包含 CSRF 令牌的隐藏输入字段,可以使用帮助函数 csrf_field 来实现:</p><pre><?php echo csrf_field(); ?></pre><p>辅助函数 csrf_field 会生成如下 HTML:</p><pre><input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"></pre><p>当然还可以使用 Blade 模板引擎提供的方式:</p><pre>{!! csrf_field() !!}</pre><p>你不需要自己编写代码去验证 POST、PUT 或者 DELETE 请求的 CSRF 令牌,因为 Laravel 自带的 HTTP 中间件 VerifyCsrfToken 会为我们做这项工作:将请求中输入的 token 值和 Session 中的存储的 token 作对比来进行验证。</p>从 CSRF 保护中排除指定 URL<p>有时候我们需要从 CSRF 保护中排除一些 URL,比如,如果你使用了 Stripe 来处理支付并用到他们的 webhook 系统,这时候就需要从 Laravel 的 CSRF 保护中排除  webhook 处理器路由。</p><p>要实现这一目的,你需要在 VerifyCsrfToken 中间件中将要排除的 URL 添加到 $except 属性:</p><pre><?php namespace AppHttpMiddleware; use IlluminateFoundationHttpMiddlewareVerifyCsrfToken as BaseVerifier; class VerifyCsrfToken extends BaseVerifier { /** *从CSRF验证中排除的URL * * @var array */ protected $except = [ 'stripe/*', ]; }</pre>X-CSRF-Token<p>除了将 CSRF 令牌作为 POST 参数进行验证外,还可以通过设置 X-CSRF-Token 请求头来实现验证,VerifyCsrfToken 中间件会检查 X-CSRF-TOKEN 请求头,首先创建一个 meta 标签并将令牌保存到该 meta 标签:</p><pre><meta name="csrf-token" content="{{ csrf_token() }}"></pre><p>然后在 js 库(如 jQuery)中添加该令牌到所有请求头,这为基于 AJAX 的应用提供了简单、方便的方式来避免 CSRF 攻击:</p><pre>$.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } });</pre>X-XSRF-Token<p>Laravel 还会将 CSRF 令牌保存到了名为 XSRF-TOKEN 的 Cookie 中,你可以使用该 Cookie 值来设置 X-XSRF-TOKEN 请求头。一些 JavaScript 框架,比如  Angular,会为你自动进行设置,基本上你不太需要手动设置这个值。</p>6、路由模型绑定<p>Laravel 路由模型绑定为注入类实例到路由提供了方便,例如,你可以将匹配给定 ID 的整个 User 类实例注入到路由中,而不是直接注入用户 ID。</p>隐式绑定<p>Laravel 会自动解析定义在路由或控制器动作(变量名匹配路由片段)中的 Eloquent 模型类型声明,例如:</p><pre>Route::get('api/users/{user}', function (AppUser $user) { return $user->email; });</pre><p>在这个例子中,由于类型声明了 Eloquent 模型 AppUser,对应的变量名 $user 会匹配路由片段中的{user},这样,Laravel 会自动注入与请求 URI 中传入的 ID 对应的用户模型实例。</p><p>如果数据库中找不到对应的模型实例,会会自动生成 HTTP 404 响应。</p><p>自定义键名</p><p>如果你想要隐式模型绑定使用数据表的其它字段,可以重写 Eloquent 模型类的 getRouteKeyName 方法:</p><pre>/** * Get the route key for the model. * * @return string */ public function getRouteKeyName() { return 'slug'; }</pre>显式绑定<p>要注册显式绑定,需要使用路由的 model 方法来为给定参数指定绑定类。应该在 RouteServiceProvider::boot 方法中定义模型绑定:</p><p>绑定参数到模型</p><pre>public function boot(Router $router) { parent::boot($router); $router->model('user', 'AppUser'); }</pre><p>接下来,定义一个包含 {user} 参数的路由:</p><pre>$router->get('profile/{user}', function(AppUser $user) { // });</pre><p>由于我们已经绑定 {user} 参数到 AppUser 模型,User 实例会被注入到该路由。因此,如果请求 URL 是 profile/1,就会注入一个用户 ID 为 1 的 User 实例。</p><p>如果匹配的模型实例在数据库不存在,会自动生成并返回 HTTP 404 响应。</p><p>自定义解析逻辑</p><p>如果你想要使用自定义的解析逻辑,需要使用 Route::bind 方法,传递到 bind 方法的闭包会获取到 URI 请求参数中的值,并且返回你想要在该路由中注入的类实例:</p><pre>$router->bind('user', function($value) { return AppUser::where('name', $value)->first(); });</pre><p>自定义“Not Found”</p><p>如果你想要指定自己的“Not Found”行为,将封装该行为的闭包作为第三个参数传递给 model 方法:</p><pre>$router->model('user', 'AppUser', function() { throw new NotFoundHttpException; });</pre>7、表单方法伪造<p>HTML 表单不支持 PUT、PATCH 或者 DELETE 请求方法,因此,当 PUT、PATCH 或 DELETE 路由时,需要添加一个隐藏的 _method 字段到表单中,其值被用作该表单的 HTTP 请求方法:</p><pre><form action="/foo/bar" method="POST"> <input type="hidden" name="_method" value="PUT"> <input type="hidden" name="_token" value="{{ csrf_token() }}"> </form></pre><p>还可以使用辅助函数 method_field 来实现这一目的:</p><pre><?php echo method_field('PUT'); ?></pre><p>当然,也支持 Blade 模板引擎:</p><pre>{{ method_field('PUT') }}</pre><br /><p>HTTP路由实例教程(二)—— 路由命名和路由分组<br /></p><p>http://laravelacademy.org/post/2784.html<br /></p><p><br /></p><p><br /></p><p>1、<a href="http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1%e5%91%bd%e5%90%8d" title="View all posts in 路由命名" target="_blank">路由命名</a>——给<a href="http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1" title="View all posts in 路由" target="_blank">路由</a>起个名字1.1 基本使用</p><p>我们使用as关键字来为路由命名:</p><pre><a href="http://laravelacademy.org/tags/route" title="View all posts in Route" target="_blank">Route</a>::get('/hello/laravelacademy',['as'=>'academy',function(){ return 'Hello <a href="http://laravelacademy.org/tags/laravel" title="View all posts in Laravel" target="_blank">Laravel</a>Academy!'; }]);</pre><p>路由命名可以让我们在使用<a href="http://laravelacademy.org/post/205.html#ipt_kb_toc_205_48">route</a>函数生成指向该路由的URL或者生成跳转到该路由的<a href="http://laravelacademy.org/tags/%e9%87%8d%e5%ae%9a%e5%90%91" title="View all posts in 重定向" target="_blank">重定向</a>链接时更加方便:</p><pre>Route::get('/testNamedRoute',function(){ return route('academy'); });</pre><p>我们在浏览器中访问http://laravel.app:8000/testNamedRoute时输出http://laravel.app:8000/hello/laravelacademy,然后我们修改上述闭包内代码:</p><pre>Route::get('/testNamedRoute',function(){ return redirect()->route('academy'); });</pre><p>再次在浏览器中访问http://laravel.app:8000/testNamedRoute时会跳转到http://laravel.app:8000/hello/laravelacademy。</p><p>我们甚至还可以在使用带参数的路由命名:</p><pre>Route::get('/hello/laravelacademy/{id}',['as'=>'academy',function($id){ return 'Hello LaravelAcademy '.$id.'!'; }]);</pre><p>对应的测试路由定义如下:</p><pre>Route::get('/testNamedRoute',function(){ return redirect()->route('academy',['id'=>1]); });</pre><p>这样,当我们在浏览器中访问http://laravel.app:8000/testNamedRoute时会跳转到http://laravel.app:8000/hello/laravelacademy/1</p>1.2 <a href="http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1%e5%88%86%e7%bb%84" title="View all posts in 路由分组" target="_blank">路由分组</a>时路由命名方式<p>再来看一个更复杂的例子,使用路由分组时如何定义路由命名?官网文档提供的例子如下:</p><pre>Route::group(['as' => 'admin::'], function () { Route::get('dashboard', ['as' => 'dashboard', function () { // }]); });</pre><p>在Route<a href="http://laravelacademy.org/post/97.html">门面</a>的group方法中使用一个as关键字来指定该路由群组中所有路由的公共<a href="http://laravelacademy.org/tags/%e5%89%8d%e7%bc%80" title="View all posts in 前缀" target="_blank">前缀</a>,然后再在里面每个路由中使用as关键字为该路由命名。</p><p>这样我们可以通过如下方式来生成该路由URL:</p><pre>Route::get('/testNamedRoute',function(){ return route('admin::dashboard'); });</pre>2、路由分组<p>路由分组就是将一组拥有相同属性(<a href="http://laravelacademy.org/tags/%e4%b8%ad%e9%97%b4%e4%bb%b6" title="View all posts in 中间件" target="_blank">中间件</a>、<a href="http://laravelacademy.org/tags/%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4" title="View all posts in 命名空间" target="_blank">命名空间</a>、<a href="http://laravelacademy.org/tags/%e5%ad%90%e5%9f%9f%e5%90%8d" title="View all posts in 子域名" target="_blank">子域名</a>、路由前缀等)的路由使用Route门面的group方法聚合起来。</p>2.1 中间件<p>首先我们在应用根目录下运行如下Artisan命令生成一个测试用的<a href="http://laravelacademy.org/post/57.html">中间件</a>TestMiddleware:</p><pre>php artisan make:middleware TestMiddleware</pre><p>这样会在/app/Http/Middleware目录下生成一个TestMiddleware.php文件,打开该文件编辑TestMiddleware类的handle方法如下:</p><pre>public function handle($request, Closure $next) { if($request->input('age')<18) return redirect()->route('refuse'); return $next($request); }</pre><p>我们在中间件中定义这段业务逻辑的目的是年龄18岁以下的未成年人不能访问。</p><p>然后我们打开/app/Http/Kernal.php文件,新增TestMiddleware到Kernel的$routeMiddleware属性:</p><pre>protected $routeMiddleware = [ 'auth' => AppHttpMiddlewareAuthenticate::class, 'auth.basic' => IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class, 'guest' => AppHttpMiddlewareRedirectIfAuthenticated::class, 'test' => AppHttpMiddlewareTestMiddleware::class, ];</pre><p>接下来我们在routes.php中定义路由如下:</p><pre>Route::group(['middleware'=>'test'],function(){ Route::get('/write/laravelacademy',function(){ //使用Test中间件 }); Route::get('/update/laravelacademy',function(){ //使用Test中间件 }); }); Route::get('/age/refuse',['as'=>'refuse',function(){ return "未成年人禁止入内!"; }]); </pre><p>这样当我们在浏览器中访问http://laravel.app:8000/write/laravelacademy?age=15或者http://laravel.app:8000/update/laravelacademy?age=15时就会跳转到http://laravel.app:8000/age/refuse,并显示:</p><pre>未成年人禁止入内!</pre>2.2 命名空间<p>默认情况下,routes.php中的定义的控制器位于AppHttpControllers命名空间下,所以如果要指定命名空间,只需指定AppHttpControllers之后的部分即可:</p><pre>Route::group(['namespace' => 'LaravelAcademy'], function(){ // 控制器在 "AppHttpControllersLaravelAcademy" 命名空间下 Route::group(['namespace' => 'DOCS'], function() { // 控制器在 "AppHttpControllersLaravelAcademyDOCS" 命名空间下 }); }); </pre>2.3 子域名<p>子域名可以通过domain关键字来设置:</p><pre>Route::group(['domain'=>'{service}.laravel.app'],function(){ Route::get('/write/laravelacademy',function($service){ return "Write FROM {$service}.laravel.app"; }); Route::get('/update/laravelacademy',function($service){ return "Update FROM {$service}.laravel.app"; }); }); </pre><p>这样我们在浏览器中访问http://write.laravel.app:8000/write/laravelacademy,则输出</p><pre>Write FROM write.laravel.app</pre><p>访问http://update.laravel.app:8000/write/laravelacademy时,则输出:</p><pre>Write FROM update.laravel.app</pre><p>注意:要想让子域名解析生效,需要在hosts中绑定IP地址</p>2.4 路由前缀<p>如果路由群组中的所有路由包含统一前缀,则我们可以通过在group方法中设置prefix属性来指定该前缀:</p><pre>Route::group(['prefix'=>'laravelacademy'],function(){ Route::get('write',function(){ return "Write LaravelAcademy"; }); Route::get('update',function(){ return "Update LaravelAcademy"; }); }); </pre><p>这样我们就可以通过http://laravel.app:8000/laravelacademy/write或者http://laravel.app:8000/laravelacademy/update来访问对应的操作。</p><p>我们甚至还可以在路由前缀中指定参数:</p><pre>Route::group(['prefix'=>'laravelacademy/{version}'],function(){ Route::get('write',function($version){ return "Write LaravelAcademy {$version}"; }); Route::get('update',function($version){ return "Update LaravelAcademy {$version}"; }); }); </pre><p>这样我们在浏览器中访问http://laravel.app:8000/laravelacademy/5.1/write,则对应会输出:</p><pre>Write LaravelAcademy 5.1</pre><br />

正文结束

没有上一篇 Laravel5中通过SimpleQrCode扩展包生成二维码实例