[TOC]
### **1、基本[路由](http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1 "View all posts in 路由")**
所有應用路由都定義在?`App\Providers\RouteServiceProvider`?類載入的?`app/Http/routes.php`?文件中。
最基本的?[Laravel](http://laravelacademy.org/tags/laravel "View all posts in Laravel")?路由接收一個 URI 和一個閉包:
~~~
Route::get('foo', function () {
return 'Hello World';
});
Route::post('foo', function () {
//
});
~~~
默認情況下,`routes.php`?文件包含單個路由和一個[路由群組](http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1%e7%be%a4%e7%bb%84 "View all posts in 路由群組"),該路由群組包含的所有路由都使用了[中間件](http://laravelacademy.org/tags/%e4%b8%ad%e9%97%b4%e4%bb%b6 "View all posts in 中間件")組?`web`,而這個中間件組為路由提供了 Session 狀態和 ?[CSRF](http://laravelacademy.org/tags/csrf "View all posts in CSRF")?保護功能。通常,我們會將所有路由定義在這個路由組中。
#### **有效的[路由方法](http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1%e6%96%b9%e6%b3%95 "View all posts in 路由方法")**
我們可以注冊路由來響應任何?[HTTP](http://laravelacademy.org/tags/http "View all posts in HTTP")?請求:
~~~
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 請求:
~~~
Route::match(['get', 'post'], '/', function () {
//
});
Route::any('foo', function () {
//
});
~~~
### **2、[路由參數](http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1%e5%8f%82%e6%95%b0 "View all posts in 路由參數")**
#### **必選參數**
有時我們需要在路由中捕獲 URI 片段。比如,要從?[URL](http://laravelacademy.org/tags/url "View all posts in URL")?中捕獲用戶 ID,需要通過如下方式定義路由參數:
~~~
Route::get('user/{id}', function ($id) {
return 'User '.$id;
});
~~~
可以按需要在路由中定義多個路由參數:
~~~
Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
//
});
~~~
路由參數總是通過花括號進行包裹,這些參數在路由被執行時會被傳遞到路由的閉包。
> 注意:路由參數不能包含?`-`?字符,需要的話可以使用?`_`?替代。
#### **可選參數**
有時候可能需要指定可選的路由參數,這可以通過在參數名后加一個?`?`?標記來實現,這種情況下需要給相應的變量指定默認值:
~~~
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
return $name;
});
~~~
### **3、命名路由**
命名路由為生成 URL 或重定向提供了便利。實現也很簡單,在定義路由時使用數組鍵?`as`?指定路由名稱:
~~~
Route::get('user/profile', ['as' => 'profile', function () {
//
}]);
~~~
此外,還可以為控制器動作指定路由名稱:
~~~
Route::get('user/profile', [
'as' => 'profile', 'uses' => 'UserController@showProfile'
]);
~~~
此外,除了在路由數組定義中指定路由名稱外,還可以通過在路由定義之后使用?`name`?方法鏈的方式來實現:
~~~
Route::get('user/profile', 'UserController@showProfile')->name('profile');
~~~
#### **路由群組 & 命名路由**
如果你在使用路由群組,可以通過在路由群組的屬性數組中指定?`as`?關鍵字來為群組中的路由設置一個共用的路由名前綴:
~~~
Route::group(['as' => 'admin::'], function () {
Route::get('dashboard', ['as' => 'dashboard', function () {
// 路由被命名為 "admin::dashboard"
}]);
});
~~~
#### **為命名路由生成URL**
如果你為給定路由進行了命名,就可以通過?`route`?函數為該命名路由生成對應 URL:
~~~
$url = route('profile');
$redirect = redirect()->route('profile');
~~~
如果命名路由定義了參數,可以將該參數作為第二個參數傳遞給?`route`?函數。給定的路由參數將會自動插入 URL 中:
~~~
Route::get('user/{id}/profile', ['as' => 'profile', function ($id) {
//
}]);
$url = route('profile', ['id' => 1]);
~~~
### **4、路由群組**
路由群組允許我們在多個路由中共享路由屬性,比如中間件和[命名空間](http://laravelacademy.org/tags/%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4 "View all posts in 命名空間")等,這樣的話我們就不必為每一個路由單獨定義屬性。共享屬性以數組的形式作為第一個參數被傳遞給?`Route::group`?方法。
下面我們通過幾個簡單的應用實例來演示路由群組。
#### **中間件**
要給路由群組中定義的所有路由分配中間件,可以在群組屬性數組中使用?`middleware`。中間件將會按照數組中定義的順序依次執行:
~~~
Route::group(['middleware' => 'auth'], function () {
Route::get('/', function () {
// 使用 Auth 中間件
});
Route::get('user/profile', function () {
// 使用 Auth 中間件
});
});
~~~
#### **命名空間**
另一個通用的例子是路由群組分配同一個 PHP 命名空間給其下的多個控制器,可以在分組屬性數組中使用`namespace`?來指定群組中所有控制器的公共命名空間:
~~~
Route::group(['namespace' => 'Admin'], function(){
// 控制器在 "App\Http\Controllers\Admin" 命名空間下
Route::group(['namespace' => 'User'], function(){
// 控制器在 "App\Http\Controllers\Admin\User" 命名空間下
});
});
~~~
默認情況下,`RouteServiceProvider`?引入?`routes.php`?并指定其下所有控制器類所在的默認命名空間`App\Http\Controllers`,因此,我們在定義的時候只需要指定命名空間?`App\Http\Controllers`?之后的部分即可。
#### **[子域名](http://laravelacademy.org/tags/%e5%ad%90%e5%9f%9f%e5%90%8d "View all posts in 子域名")路由**
路由群組還可以被用于子域名路由通配符,子域名可以像 URI 一樣被分配給路由參數,從而允許捕獲子域名的部分用于路由或者控制器,子域名可以通過群組屬性數組中的?`domain`?來指定:
~~~
Route::group(['domain' => '{account}.myapp.com'], function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
~~~
#### **路由前綴**
群組屬性?`prefix`?可以用來為群組中每個路由添加一個給定 URI 前綴,比如,你可以為所有路由 URI 添加?`admin`?前綴 :
~~~
Route::group(['prefix' => 'admin'], function () {
Route::get('users', function () {
// 匹配 "/admin/users" URL
});
});
~~~
你還可以使用?`prefix`?參數為路由群組指定公共路由參數:
~~~
Route::group(['prefix' => 'accounts/{account_id}'], function () {
Route::get('detail', function ($account_id) {
// 匹配 accounts/{account_id}/detail URL
});
});
~~~
### **5、CSRF 攻擊**
#### **簡介**
跨站請求偽造是一種通過偽裝授權用戶的請求來利用授信網站的惡意漏洞。Laravel 使得防止應用遭到跨站請求偽造攻擊變得簡單。
Laravel 自動為每一個被應用管理的有效用戶會話生成一個 CSRF “令牌”,該令牌用于驗證授權用戶和發起請求者是否是同一個人。想要生成包含 CSRF 令牌的隱藏輸入字段,可以使用幫助函數?`csrf_field`?來實現:
~~~
<?php echo csrf_field(); ?>
~~~
輔助函數?`csrf_field`?會生成如下 HTML:
~~~
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
~~~
當然還可以使用 Blade 模板引擎提供的方式:
~~~
{!! csrf_field() !!}
~~~
你不需要自己編寫代碼去驗證 POST、PUT 或者 DELETE 請求的 CSRF 令牌,因為 Laravel 自帶的 HTTP 中間件`VerifyCsrfToken`?會為我們做這項工作:將請求中輸入的?`token`?值和 Session 中的存儲的?`token`?作對比來進行驗證。
#### **從 CSRF 保護中排除指定 URL**
有時候我們需要從 CSRF 保護中排除一些 URL,比如,如果你使用了 Stripe 來處理支付并用到他們的 webhook 系統,這時候就需要從 Laravel 的 CSRF 保護中排除 ?webhook 處理器路由。
要實現這一目的,你需要在?`VerifyCsrfToken`?中間件中將要排除的 URL 添加到?`$except`?屬性:
~~~
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
/**
*從CSRF驗證中排除的URL
*
* @var array
*/
protected $except = [
'stripe/*',
];
}
~~~
#### **X-CSRF-Token**
除了將 CSRF 令牌作為 POST 參數進行驗證外,還可以通過設置?`X-CSRF-Token`?請求頭來實現驗證,`VerifyCsrfToken`?中間件會檢查?`X-CSRF-TOKEN`?請求頭,首先創建一個 meta 標簽并將令牌保存到該 meta 標簽:
~~~
<meta name="csrf-token" content="{{ csrf_token() }}">
~~~
然后在 js 庫(如 jQuery)中添加該令牌到所有請求頭,這為基于 AJAX 的應用提供了簡單、方便的方式來避免 CSRF 攻擊:
~~~
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
~~~
#### **X-XSRF-Token**
Laravel 還會將 CSRF 令牌保存到了名為?`XSRF-TOKEN`?的 Cookie 中,你可以使用該 Cookie 值來設置?`X-XSRF-TOKEN`請求頭。一些 JavaScript 框架,比如 ?Angular,會為你自動進行設置,基本上你不太需要手動設置這個值。
### **6、路由[模型綁定](http://laravelacademy.org/tags/%e6%a8%a1%e5%9e%8b%e7%bb%91%e5%ae%9a "View all posts in 模型綁定")**
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`?方法:
~~~
/**
* Get the route key for the model.
*
* @return string
*/
public function getRouteKeyName()
{
return 'slug';
}
~~~
#### **顯式綁定**
要注冊顯式綁定,需要使用路由的?`model`?方法來為給定參數指定綁定類。應該在?`RouteServiceProvider::boot`?方法中定義模型綁定:
**綁定參數到模型**
~~~
public function boot(Router $router)
{
parent::boot($router);
$router->model('user', 'App\User');
}
~~~
接下來,定義一個包含?`{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 請求參數中的值,并且返回你想要在該路由中注入的類實例:
~~~
$router->bind('user', function($value) {
return App\User::where('name', $value)->first();
});
~~~
**自定義“Not Found”**
如果你想要指定自己的“Not Found”行為,將封裝該行為的閉包作為第三個參數傳遞給?`model`?方法:
~~~
$router->model('user', 'App\User', function() {
throw new NotFoundHttpException;
});
~~~
### **7、表單方法偽造**
HTML 表單不支持 PUT、PATCH 或者 DELETE 請求方法,因此,當 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>
~~~
還可以使用輔助函數?`method_field`?來實現這一目的:
~~~
<?php echo method_field('PUT'); ?>
~~~
當然,也支持 Blade 模板引擎:
~~~
{{ method_field('PUT') }}
~~~
- 序言
- 發行版本說明
- 升級指南
- 貢獻代碼
- 開始
- 安裝
- 配置
- Laravel Homestead
- 基礎
- HTTP 路由
- HTTP 中間件
- HTTP 控制器
- HTTP 請求
- HTTP 響應
- 視圖
- Blade 模板引擎
- 架構
- 一次請求的生命周期
- 應用目錄結構
- 服務提供者
- 服務容器
- 門面(Facades)
- 數據庫
- 起步
- 查詢構建器
- 遷移
- 填充數據
- Eloquent ORM
- 起步
- 關聯關系
- 集合
- 訪問器&修改器
- 序列化
- 服務
- 用戶認證
- 用戶授權
- Artisan Console
- 訂閱支付實現:Laravel Cashier
- 緩存
- 集合
- 集成前端資源:Laravel Elixir
- 加密
- 錯誤&日志
- 事件
- 文件系統/云存儲
- 哈希
- 輔助函數
- 本地化
- 郵件
- 包開發
- 分頁
- Redis
- 隊列
- Session
- Envoy Task Runner
- 任務調度
- 測試
- 驗證
- 新手入門指南
- 簡單任務管理系統
- 帶用戶功能的任務管理系統