# 基礎 —— HTTP 路由
## 1、基本路由
大部分路由都定義在被`App\Providers\RouteServiceProvider`類載入的`app/Http/routes.php`文件中。
最基本的Laravel路由接收一個URI和一個閉包:
~~~
Route::get('/', function () {
return 'Hello World';
});
Route::post('foo/bar', function () {
return 'Hello World';
});
Route::put('foo/bar', function () {
//
});
Route::delete('foo/bar', function () {
//
});
~~~
**為多個動作注冊路由**
有時候需要注冊一個路由來響應多個不同的HTTP動作,你可以使用`Route`[門面](http://laravelacademy.org/post/97.html)的`match`方法來實現:
~~~
Route::match(['get', 'post'], '/', function () {
return 'Hello World';
});
~~~
或者,還可以使用`any`方法注冊一個路由響應所有HTTP動作:
~~~
Route::any('foo', function () {
return 'Hello World';
});
~~~
**生成路由對應的URLs**
可以使用幫助函數`url`來生成路由對應的URLs:
~~~
$url = url('foo');
~~~
## 2、路由參數
### 2.1 必選參數
有時我們需要在路由中捕獲URI片段,比如,如果想要從URL中捕獲用戶ID,可以通過如下方式定義路由參數:
~~~
Route::get('user/{id}', function ($id) {
return 'User '.$id;
});
~~~
可以按需要定義在路由中定義多個路由參數:
~~~
Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
//
});
~~~
路由參數總是通過花括號進行包裹,參數在路由被執行時會被傳遞到路由的閉包。
> 注意:路由參數不能包含’-‘字符,需要的話可以使用_替代。
### 2.2 可選參數
有時候可能需要指定路由參數,并且使得該路由參數是可選的,可以通過在參數名后加一個?來標記:
~~~
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
return $name;
});
~~~
### 2.3 正則約束
可以使用路由實例上的`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]+']);
~~~
### 2.3.1 全局約束
如果想要路由參數在全局范圍內被給定正則表達式約束,可以使用`pattern`方法。可以在`RouteServiceProvider`類的`boot`方法中定義約束模式:
~~~
/**
* 定義路由模型綁定,模式過濾器等
*
* @param \Illuminate\Routing\Router $router
* @return void
* @translator http://laravelacademy.org
*/
public function boot(Router $router){
$router->pattern('id', '[0-9]+');
parent::boot($router);
}
~~~
一旦模式被定義,將會自動應用到所有包含該參數名的路由中。
> 擴展閱讀:實例教程——[HTTP路由實例教程(一)—— 基本使用及路由參數](http://laravelacademy.org/post/398.html)
## 3、****命名****路由
命名路由使生成URLs或者重定向到指定路由變得很方便,在定義路由時指定路由名稱,然后使用數組鍵`as`指定路由別名:
~~~
Route::get('user/profile', ['as' => 'profile', function () {
//
}]);
~~~
還可以為控制器動作指定路由名稱:
~~~
Route::get('user/profile', [
'as' => 'profile', 'uses' => 'UserController@showProfile'
]);
~~~
### 3.1?路由分組?& 命名路由
如果你在使用[路由分組](http://laravelacademy.org/post/53.html#route-groups),可以在路由分組屬性數組中指定as關鍵字來為分組中的路由設置一個共用的路由名前綴:
~~~
Route::group(['as' => 'admin::'], function () {
Route::get('dashboard', ['as' => 'dashboard', function () {
// 路由被命名為 "admin::dashboard"
}]);
});
~~~
### 3.2 為命名路由生成URLs
一旦你為給定路由分配了名字,通過route函數生成URLs時就可以使用路由名字:
~~~
$url = route('profile');
$redirect = redirect()->route('profile');
~~~
如果路由定義了參數,可以將路由參數作為第二個參數傳遞給`route`函數。給定的路由參數將會自動插入URL中:
~~~
Route::get('user/{id}/profile', ['as' => 'profile', function ($id) {
//
}]);
$url = route('profile', ['id' => 1]);
~~~
## 4、路由分組
路由分組允許我們在多個路由中共享路由屬性,比如中間件等,這樣的話一大波共享屬性的路由就不必再各自定義這些屬性。共享屬性以數組的形式被作為第一個參數傳遞到`Route::group`方法中。
想要了解更多路由分組,我們希望通過幾個簡單的應用實例來展示其特性。
### 4.1 中間件
要分配中間件給分組中的所有路由,可以在分組屬性數組中使用`middleware`鍵。中間件將會按照數組中定義的順序依次執行:
~~~
Route::group(['middleware' => 'auth'], function () {
Route::get('/', function () {
// 使用 Auth 中間件
});
Route::get('user/profile', function () {
// 使用 Auth 中間件
});
});
~~~
### 4.2 命名空間
另一個通用的例子是路由分組分配同一個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`之后的一部分。
### 4.3?子域名路由
路由分組還可以被用于子域名路由通配符,子域名可以像URIs一樣被分配給路由參數,從而允許捕獲子域名的部分用于路由或者控制器,子域名可以通過分組屬性數組中的`domain`鍵來指定:
~~~
Route::group(['domain' => '{account}.myapp.com'], function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
~~~
### 4.4 路由前綴
屬性`prefix`可以用來為分組中每個給定URI添加一個前綴,比如,你想要為所有路由URIs前面添加前綴`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
});
});
~~~
> 擴展閱讀:實例教程——[HTTP路由實例教程(二)—— 路由命名和路由分組](http://laravelacademy.org/post/417.html)
## 5、防止CSRF攻擊
### 5.1?簡介
Laravel使得防止應用遭到[跨站請求偽造攻擊](http://en.wikipedia.org/wiki/Cross-site_request_forgery)變得簡單。跨站請求偽造是一種通過偽裝授權用戶的請求來利用授信網站的惡意漏洞。
Laravel自動為每一個被應用管理的有效用戶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/post/57.html)`VerifyCsrfToken`會為我們做這項工作:將請求中輸入的`token`值和session中的存儲的作對比。
### 5.2 從CSRF保護中排除URIs
有時候我們想要從CSRF保護中排除一些URIs,比如,如果你在使用[Stripe](https://stripe.com/)來處理支付并用到他們的webhook系統,這時候你就需要從Laravel的CSRF保護中排除webhook處理器路由。
你可以通過在`VerifyCsrfToken`中間件中將要排除的URIs添加到`$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/*',
];
}
~~~
### 5.3 X-CSRF-Token
除了將CSRF令牌作為一個POST參數進行檢查,Laravel的`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.4 X-XSRF-Token
Laravel還將CSRF令牌保存到了名為`XSRF-TOKEN`的cookie中,你可以使用該cookie值來設置`X-XSRF-TOKEN`請求頭。一些JavaScript框架,比如Angular,將會為你自動進行設置,基本上你不太會手動設置這個值。
> 擴展閱讀:實例教程——[HTTP路由實例教程(三)—— CSRF攻擊原理及其防護](http://laravelacademy.org/post/525.html)
## 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/195.html#ipt_kb_toc_195_8)一節。
- 前言
- 序言
- 序言 ―― 發行版本說明
- 序言 ―― 升級指南
- 序言 ―― 貢獻代碼
- 開始
- 開始 ―― 安裝及配置
- 開始 ―― Laravel Homestead
- 基礎
- 基礎 ―― HTTP路由
- 基礎 ―― HTTP 中間件
- 基礎 ―― HTTP 控制器
- 基礎 ―― HTTP 請求
- 基礎 ―― HTTP 響應
- 基礎 ―― 視圖
- 基礎 ―― Blade模板
- 架構
- 架構 ―― 一次請求的生命周期
- 架構 ―― 應用目錄結構
- 架構 ―― 服務提供者
- 架構 ―― 服務容器
- 架構 ―― 契約
- 架構 ―― 門面
- 數據庫
- 數據庫 ―― 起步
- 數據庫 ―― 查詢構建器
- 數據庫 ―― 遷移
- 數據庫 ―― 填充數據
- Eloquent ORM
- Eloquent ORM ―― 起步
- Eloquent ORM ―― 關聯關系
- Eloquent ORM ―― 集合
- Eloquent ORM ―― 調整器
- Eloquent ORM ―― 序列化
- 服務
- 服務 ―― 用戶認證
- 服務 ―― Artisan 控制臺
- 服務 ―― Laravel Cashier(交易)
- 服務 ―― 緩存
- 服務 ―― 集合
- 服務 ―― Laravel Elixir
- 服務 ―― 加密
- 服務 ―― 錯誤&日志
- 服務 ―― 事件
- 服務 ―― 文件系統/云存儲
- 服務 ―― 哈希
- 服務 ―― 幫助函數
- 服務 ―― 本地化
- 服務 ―― 郵件
- 服務 ―― 包開發
- 服務 ―― 分頁
- 服務 ―― 隊列
- 服務 ―― Redis
- 服務 ―― Session
- 服務 ―― Envoy 任務運行器(SSH任務)
- 服務 ―― 任務調度
- 服務 ―― 測試
- 服務 ―― 驗證