[TOC]
## 1、基本路由
大部分路由都定義在`bootstrap/app.php`文件載入的`app/Http/routes.php`中。
最基本的[Lumen](http://laravelacademy.org/tags/lumen "View all posts in Lumen")路由接收一個URI和一個閉包:
~~~
$app->get('/', function () {
return 'Hello World';
});
$app->post('foo/bar', function () {
return 'Hello World';
});
$app->put('foo/bar', function () {
//
});
$app->delete('foo/bar', function () {
//
});
~~~
**生成指向路由對應的[URL](http://laravelacademy.org/tags/url "View all posts in URL")**
可以使用幫助函數`url`來生成路由對應的URL:
~~~
$url = url('foo');
~~~
## 2、路由參數
### 2.1 必選參數
有時我們需要在路由中捕獲URI片段,比如,如果想要從URL中捕獲用戶ID,可以通過如下方式定義路由參數:
~~~
$app->get('user/{id}', function ($id) {
return 'User '.$id;
});
~~~
可以按需要定義在路由中定義多個路由參數:
~~~
$app->get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
//
});
~~~
路由參數總是通過花括號進行包裹,參數在路由被執行時會被傳遞到路由的閉包。
> 注意:路由參數不能包含’-‘字符,需要的話可以使用_替代。
### 2.2 正則約束
可以通過在路由中定義定義正則表達式來約束路由參數格式:
~~~
$app->get('user/{name:[A-Za-z]+}', function ($name) {
//
});
~~~
## 3、命名路由
命名路由使生成URL或者重定向到指定路由變得很方便,在定義路由時指定路由名稱,然后使用數組鍵`as`指定路由別名:
~~~
$app->get('user/profile', ['as' => 'profile', function () {
//
}]);
~~~
還可以為控制器動作指定路由名稱:
~~~
$app->get('user/profile', [
'as' => 'profile', 'uses' => 'UserController@showProfile'
]);
~~~
**生成指向命名路由的URL**
一旦你為給定路由分配了名字,通過route函數生成URL時就可以使用路由名字:
~~~
$url = route('profile');
$redirect = redirect()->route('profile');
~~~
如果路由定義了參數,可以將路由參數作為第二個參數傳遞給`route`函數。給定的路由參數將會自動插入URL中:
~~~
$app->get('user/{id}/profile', ['as' => 'profile', function ($id) {
//
}]);
$url = route('profile', ['id' => 1]);
~~~
## 4、路由分組
路由分組允許我們在多個路由中共享路由屬性,比如[中間件](http://laravelacademy.org/tags/%e4%b8%ad%e9%97%b4%e4%bb%b6 "View all posts in 中間件")和[命名空間](http://laravelacademy.org/tags/%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4 "View all posts in 命名空間")等,這樣的話一大波共享屬性的路由就不必再各自定義這些屬性。共享屬性以數組的形式被作為第一個參數傳遞到`$app->group`方法中。
想要了解更多路由分組,我們希望通過幾個簡單的應用實例來展示其特性。
### 4.1 中間件
要分配中間件給分組中的所有路由,可以在分組屬性數組中使用`middleware`鍵。中間件將會按照數組中定義的順序依次執行:
~~~
$app->group(['middleware' => 'auth'], function () {
$app->get('/', function () {
// 使用 Auth 中間件
});
$app->get('user/profile', function () {
// 使用 Auth 中間件
});
});
~~~
### 4.2 命名空間
另一個通用的例子是路由分組分配同一個PHP命名空間給多個控制器,可以在分組屬性數組中使用`namespace`參數來指定分組中控制器的命名空間:
~~~
$app->group(['namespace' => 'Admin'], function(){
// 控制器在 "App\Http\Controllers\Admin" 命名空間下
$app->group(['namespace' => 'User'], function()
{
// 控制器在 "App\Http\Controllers\Admin\User" 命名空間下
});
});
~~~
默認情況下,`RouteServiceProvider`包含`routes.php`并指定其所在命名空間為App\Http\Controllers,因此,我們只需要指定`App\Http\Controllers`之后的相對命名空間即可。
### 4.3 路由前綴
屬性`prefix`可以用來為分組中每個給定URI添加一個前綴,比如,你想要為所有路由URI前面添加前綴`admin`:
~~~
$app->group(['prefix' => 'admin'], function () {
$app->get('users', function () {
// 匹配 "/admin/users" URL
});
});
~~~
你還可以使用`prefix`參數為分組路由指定公共參數:
~~~
$app->group(['prefix' => 'accounts/{account_id}'], function () {
$app->get('detail', function ($account_id) {
// 匹配 accounts/{account_id}/detail URL
});
});
~~~
## 5、CSRF攻擊及保護
> 注意:使用Lumen的該特性之前必須開啟session。
### 5.1?簡介
Lumen使得防止應用遭到[跨站請求偽造攻擊](http://en.wikipedia.org/wiki/Cross-site_request_forgery)變得簡單。跨站請求偽造是一種通過偽裝授權用戶的請求來利用授信網站的惡意漏洞。
Lumen自動為每一個被應用管理的有效用戶Session生成一個CSRF“令牌”,該令牌用于驗證授權用戶和發起請求者是否是同一個人。想要生成包含CSRF令牌的隱藏輸入字段,可以使用幫助函數`csrf_field`來實現:
~~~
<?php echo csrf_field(); ?>
~~~
幫助函數`csrf_field`生成如下HTML:
~~~
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
~~~
當然還可以使用[Blade模板引擎](http://laravelacademy.org/post/79.html)提供的方式:
~~~
{!! csrf_field() !!}
~~~
你不需要了解在POST、PUT或者DELETE請求時CSRF令牌是如何進行驗證的,[HTTP](http://laravelacademy.org/tags/http "View all posts in HTTP")中間件`VerifyCsrfToken`會為我們做這項工作:將請求中輸入的`token`值和session中的存儲的作對比。
### 5.2 X-CSRF-Token
除了將CSRF令牌作為一個POST參數進行檢查,Lumen的`VerifyCsrfToken`中間件還會檢查`X-CSRF-TOKEN`請求頭,你可以將令牌保存在”meta”標簽中:
~~~
<meta name="csrf-token" content="{{ csrf_token() }}">
~~~
創建完這個meta標簽后,就可以在js庫如jQuery中添加該令牌到所有請求頭,這為基于AJAX的應用提供了簡單、方便的方式來避免CSRF攻擊:
~~~
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
~~~
### 5.3 X-XSRF-Token
Lumen還將CSRF令牌保存到了名為`XSRF-TOKEN`的cookie中,你可以使用該cookie值來設置`X-XSRF-TOKEN`請求頭。一些JavaScript框架,比如Angular,將會為你自動進行設置,基本上你不太會手動設置這個值。
## 6、表單方法偽造
HTML表單不支持`PUT`、`PATCH`或者`DELETE`動作,因此,當定義被HTML表單調用的`PUT`、`PATCH`或`DELETE`路由時,需要添加一個隱藏的`_method`字段到給表單中,其值被用作HTTP請求方法名:
~~~
<form action="/foo/bar" method="POST">
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>
~~~
## 7、拋出404錯誤
有兩者方法手動從路由觸發404錯誤。
第一種,使用幫助函數`abort`,`abort`函數會拋出一個指定狀態碼的`Symfony\Component\HttpFoundation\Exception\HttpException`:
~~~
abort(404);
~~~
第二種,手動拋出`Symfony\Component\HttpKernel\Exception\NotFoundHttpException`.的實例。
更多關于處理404異常的信息以及如何自定義視圖顯示這些錯誤信息,請查看[錯誤文檔](http://laravelacademy.org/post/465.html)一節。