---恢復內容開始---php
路由實現邏輯在:
app/Providers/RouteServiceProvider
路由入門
最基本的 Laravel 路由只接收一個 URI 和一個閉包,並以此爲基礎提供一個很是簡單優雅的路由定義方法:
Web 界面的路由 routes/web.php
定義了 Web 界面的路由,這些路由被分配到了 web 中間件組,從而可使用 Session 和 CSRF 保護等功能。
例:
URL:/hello Route::get('hello', function () { return 'Hello, World'; });
API的路由 routes/api.php
API路由是無狀態的,這是由於被分配到了 api 中間件組。
API路由嵌套在一個路由羣組中,全部路由會被自動添加 /api 前綴,因此你不須要再到路由文件中爲每一個路由手動添加,你能夠經過編輯 類來修改路由前綴以及其餘的路由羣組選項:RouteServiceProvider
有效的路由方法:mysql
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 請求動做:laravel
Route::match(['get', 'post'], 'foo', function () { return 'This is a request from get or post'; }); Route::any('bar', function () { return 'This is a request from any HTTP verb'; });
若是上面的路由是定義在 routes/web.php
的話,在測試 POST 請求以前,須要將對應路由取消 CSRF 保護檢查,不然會返回 419
狀態碼致使沒法請求成功,取消的方法是在 app/Http/Middleware/VerifyCsrfToken
中設置排除檢查路由:web
若是你須要定義一個重定向到其餘 URI 的路由,可使用 Route::redirect
方法,該方法很是方便,以致於你不須要再定義額外的路由或控制器來執行簡單的重定向邏輯:正則表達式
Route::redirect('/here', '/there'); # 表示原路由, 表示重定向以後的路由。herethere
Route::redirect('/here', '/there',301); #默認 返回 狀態碼,使用可選的第三個參數定義狀態碼Route::redirect302
Route::permanentRedirect('/here', '/there'); #或者使用Route::permanentRedirect
方法來返回301
狀態碼Route::permanentRedirect301
若是你的路由須要返回一個視圖,可使用 Route::view
方法,和 Route::redirect
方法相似,這個方法很方便,以致於你不須要在額外定義一個路由或控制器。sql
view
方法接收一個 URI 做爲第一個參數,以及一個視圖名稱做爲第二個參數,此外,你還能夠提供一個數組數據傳遞到該視圖方法做爲可選的第三個參數,該數組數據可用於視圖中的數據渲染:數據庫
Route::view('/welcome', 'welcome');
Route::view('/welcome', 'welcome', ['name' => '博客園']);
有時須要在路由中獲取 URI 請求參數。api
例如,若是要從 URL 中獲取用戶ID,須要經過以下方式定義路由參數:數組
URL : user/5
Route::get('user/{id}', function ($id) { return 'User ' . $id; });
能夠根據須要在路由中定義多個路由參數:瀏覽器
URL: /user/5/group/3
Route::get('user/{userid}/group/{groupid}', function ($userId, $groupId) { return 'userid:'.$userId.';groupid:'.$groupId.PHP_EOL; });
路由參數名稱不能包含 -
字符,若是須要的話可使用 _
替代,好比若是某個路由參數定義成 {user-id}
則訪問路由會報錯,應該修改爲 {user_id}
才行。
路由參數被注入到路由回調/控制器取決於它們的順序,與回調/控制器名稱無關。
可選參數能夠經過在參數名後加一個 ?
標記來實現,這種狀況下須要給相應的變量指定默認值,當對應的路由參數爲空時,使用默認值:
Route::get('user/{name?}', function ($name = 'mrzhao') { return $name; });
URL : /user/wang #wang
/user #mrzhao
能夠經過路由實例上的 where
方法來約束路由參數的格式。where
方法接收參數名和一個正則表達式來定義該參數如何被約束:
Route::get('user/{name}', function ($name) { // $name 必須是字母且不能爲空 return $name; })->where('name', '[A-Za-z]+');
Route::get('user/{id}', function ($id) {
// $id 必須是數字
return $id;
})->where('id', '[0-9]+');
#多個參數
URL : user/123/wan
Route::get('user/{id}/{name}', function ($id, $name) {
// 同時指定 id 和 name 的數據格式
return 'id:'.$id.';name:'.$name.PHP_EOL;
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
使用正則約束還有一個好處就是避免了 user/{id}
和 user/{name}
的混淆。
全局約束
若是想要路由參數在全局範圍內被給定正則表達式約束,可使用 pattern
方法。須要在 RouteServiceProvider
類的 boot
方法中定義這種約束模式:
/** * 定義路由模型綁定,模式過濾器等 * * @param \Illuminate\Routing\Router $router * @return void * @translator http://laravelacademy.org */ public function boot() { Route::pattern('id', '[0-9]+'); parent::boot(); }
一旦模式被定義,將會自動應用到全部包含該參數名的路由中:
Route::get('user/{id}/{name}', function ($id, $name) { // 一旦模式被定義,將會自動應用到全部包含該參數名的路由中:
// 若是還有其餘參數,再繼續指定 name 的數據格式 return 'id:'.$id.';name:'.$name.PHP_EOL; })->where('name' , '[a-z]+');
//除此以外,該模式還會被應用到諸以下面這些路由參數上:
Route::get('post/{id}', function ($id) {
// 只有當 {id} 是數字時纔會被調用
return $id;
});
Route::get('product/{id}', function ($id) {
// 只有當 {id} 是數字時纔會被調用
return $id;
});
很顯然這種方式讓代碼更簡潔,也爲咱們實現同一參數統一約束帶來了方便。
編碼/
路由組件支持除 /
以外的全部字符,若是要在佔位符中使用 /
須要經過 where
條件正則表達式顯式容許:
Route::get('search/{search}', function ($search) { return $search; })->where('search', '.*');
注:只有最後一個路由參數片斷中才支持編碼正斜槓/
。
命名路由爲生成 URL 或重定向提供了方便,實現起來也很簡單,在路由定義以後使用 name
方法鏈的方式來定義該路由的名稱:
Route::get('user/profile', function () { // 經過路由名稱生成 URL return 'my url: ' . route('profile'); })->name('profile');
還能夠爲控制器動做指定路由名稱:
Route::get('user/profile', 'UserController@showProfile')->name('profile');
經過如下方式定義重定向:
Route::get('redirect', function() { // 經過路由名稱進行重定向 return redirect()->route('profile'); });
爲命名路由生成 URL
如上面代碼所展現的,爲給定路由分配名稱以後,就能夠經過輔助函數 route
爲該命名路由生成 URL 或者經過 redirect
函數進行重定向:
// 生成URL $url = route('profile'); // 生成重定向 return redirect()->route('profile');
若是命名路由定義了參數,能夠將該參數做爲第二個參數傳遞給 route
函數。給定的路由參數將會自動插入到 URL 中:
Route::get('user/{id}/profile', function ($id) { $url = route('profile', ['id' => $id]); return $url; })->name('profile');
檢查當前路由
想要判斷當前請求是否被路由到給定命名路由,可使用 Route 實例上的 named
方法,例如,你能夠從路由中間件中檢查當前路由名稱:
/** * 處理輸入請求 * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { if ($request->route()->named('profile')) { // } return $next($request); }
路由分組的目的是讓咱們在多個路由中共享相同的路由屬性,好比中間件和命名空間等,這樣的話咱們定義了大量的路由時就沒必要爲每個路由單獨定義屬性。共享屬性以數組的形式做爲第一個參數被傳遞給 Route::group
方法。
嵌套的分組會嘗試智能地將屬性合併到父分組中,中間件和 where
條件會直接被合併,而路由命名、命名空間、以及路由前綴會被附加到父組件對應屬性以後。命名空間分隔符和 URI 中的斜槓會被自動添加到合適的位置。
要給某個路由分組中定義的全部路由分配中間件,能夠在定義分組以前使用 middleware
方法。中間件將會按照數組中定義的順序依次執行:
Route::middleware(['first', 'second'])->group(function () { Route::get('/', function () { // Uses first & second Middleware }); Route::get('user/profile', function () { // Uses first & second Middleware }); });
關於中間件的使用在後面單獨講中間件時再進行示例演示,先了解這樣使用就行。
路由分組另外一個通用的例子是使用 namespace
方法分配同一個 PHP 命名空間給該分組下的多個控制器:
Route::namespace('Admin')->group(function () { // Controllers Within The "App\Http\Controllers\Admin" Namespace });
默認狀況下,RouteServiceProvider
在一個命名空間分組下引入全部路由文件,並指定全部控制器類所在的默認命名空間是 App\Http\Controllers
,所以,咱們在定義控制器的時候只須要指定命名空間 App\Http\Controllers
以後的部分便可。
路由分組還能夠被用於處理子域名路由,子域名能夠像 URI 同樣被分配給路由參數,從而容許捕獲子域名的部分用於路由或者控制器,子域名能夠在定義分組以前調用 domain
方法來指定:
#好比設置會員子域名爲 ,那麼就能夠經過 訪問用戶ID爲 的會員信息了
#account.blog.testhttp://account.blog.test/user/11This is account page of User 1
Route::domain('{account}.blog.dev')->group(function () { Route::get('user/{id}', function ($account, $id) { return 'This is ' . $account . ' page of User ' . $id; }); });
prefix
方法能夠用來爲分組中每一個路由添加一個給定 URI 前綴,例如,你能夠爲分組中全部路由 URI 添加 admin
前綴 :
URL : /admin/users
Route::prefix('admin')->group(function () { Route::get('users', function () { // Matches The "/admin/users" URL }); });
name
方法可經過傳入字符串爲分組中的每一個路由名稱設置前綴,例如,你可能想要在全部分組路由的名稱前添加 admin
前綴,因爲給定字符串和指定路由名稱前綴字符串徹底同樣,因此須要在前綴字符串末尾後加上 .
字符:
Route::name('admin.')->group(function () { Route::get('users', function () { // 新的路由名稱爲 "admin.users"... })->name('users'); });
注入模型 ID 到路由或控制器動做時,一般須要查詢數據庫才能獲取相應的模型數據。Laravel 路由模型綁定讓注入模型實例到路由變得簡單。
例如,你能夠將匹配給定 ID 的整個 User
類實例注入到路由中,而不僅是注入用戶 ID。
Laravel 會自動解析定義在路由或控制器動做(變量名匹配路由片斷)中的 Eloquent 模型類型聲明,例如(咱們將這個路由定義在 routes/api.php
文件中):
Route::get('users/{user}', function (App\User $user) { dd($user);
// return $user->email; });
在這個例子中,因爲類型聲明瞭 Eloquent 模型 App\User
,對應的變量名 $user
會匹配路由片斷中的 {user}
,這樣,Laravel 會自動注入與請求 URI 中傳入的 ID 對應的用戶模型實例。若是匹配模型實例在數據庫中不存在,會自動生成 404 響應。
在演示本功能以前,咱們須要先建立數據表,因爲我是在 Valet 開發環境中開發,須要本身建立數據庫,咱們將數據庫命名爲 testlv,本地的數據庫用戶名爲 root
,密碼爲123456,對應地,修改 .env
文件配置以下:
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=valet DB_USERNAME=root DB_PASSWORD=123456
基於 Laravel 強大的數據庫遷移功能建立 users
表,關於數據庫遷移後面在數據庫部分會詳細討論,這裏咱們經過如下命令來生成 users
表便可:
php artisan migrate
這時,users
數據表尚未任何記錄,若是數據庫中找不到對應的模型實例,會自動生成 HTTP 404 響應,提示頁面不存在,因此咱們須要在這張表中插入一條記錄,這裏咱們基於 Laravel 強大的數據庫填充器來快速完成數據填充功能,首先經過以下命令生成 users
對應的數據表填充器:
php artisan migrate
進入數據庫能夠看到該表已經生成:
users
數據表尚未任何記錄,若是數據庫中找不到對應的模型實例,會自動生成 HTTP 404 響應,提示頁面不存在,因此須要在這張表中插入一條記錄,這裏咱們基於 Laravel 強大的數據庫填充器來快速完成數據填充功能,首先經過以下命令生成 users
對應的數據表填充器:
php artisan make:seeder UsersTableSeeder
該命令會在 database/seeds
目錄下生成一個 UsersTableSeeder
文件,編輯該文件內容以下:
而後編輯同目錄下的 DatabaseSeeder.php
文件以下(取消調用數據表填充器前的註釋):
php artisan db:seed
php artisan db:seed --class=UsersTableSeeder #上面命令無論用 再試這條
執行上面的命令便可插入對應數據到 users
表了,這樣在瀏覽器中再次訪問 api/users/1
的時候就會顯示模型數據了:
接下來,你就能夠在應用代碼中直接拿 $user
模型去作你想作的事情了,而不須要本身去數據庫查詢,從而提升了開發的效率。
自定義鍵名
若是你想要在隱式模型綁定中使用數據表的其它字段而不是 id
字段,能夠重寫 Eloquent 模型類的 getRouteKeyName
方法,以 User
模型爲例,能夠在該模型類中添加這個方法 :
//若是是name
//首先修改api.php
$router->get('users/{name}', function(App\User $name) {
dd($name);
}); //其次修改app\User.php 模型文件
/* * Get the route key for the model. * * @return string
*/ public function getRouteKeyName() { return 'name'; }
這樣咱們就能夠經過 users/lisi
訪問同一個模型實例了。這裏須要注意的點是若是該字段不是惟一鍵,則會返回結果集的第一條記錄,對應的底層實如今這裏:
有隱式綁定,就有顯式綁定。要註冊顯式綁定,可使用路由器的 model
方法來爲給定參數指定綁定類。你須要在 RouteServiceProvider
類的 boot
方法中定義顯式模型綁定:
public function boot() { parent::boot(); Route::model('user_model', \App\User::class); }
接下來,在 routes/api.php
中定義一個包含 {user}
參數的路由:
$router->get('profile/{user_model}', function(App\User $user) { dd($user); });
因爲已經綁定 {user_model}
參數到 App\User
模型,User
實例會被注入到該路由。所以,請求 URL 是 /api/profile/1
,就會注入一個用戶 ID 爲 1
的 User
實例。
若是匹配的模型實例在數據庫不存在,會自動生成並返回 HTTP 404 響應。
自定義解析邏輯
若是你想要使用自定義的解析邏輯,能夠在 RouteServiceProvider
類的 boot
方法中使用 Route::bind
方法,傳遞到 bind
方法的閉包會獲取到 URI 請求參數中的值,而且返回你想要在該路由中注入的類實例:
public function boot() { parent::boot(); Route::bind('user', function($value) { return App\User::where('name', $value)->first() ?? abort(404); }); }
此外,你還能夠在 Eloquent 模型中覆蓋 resolveRouteBinding
方法,這個方法會獲取 URI 片斷中的值並返回應該被注入的路由模型類實例:
/** * Retrieve the model for a bound value. * * @param mixed $value * @return \Illuminate\Database\Eloquent\Model|null */ public function resolveRouteBinding($value) { return $this->where('name', $value)->first() ?? abort(404); }
使用 Route::fallback
方法能夠定義一個當全部其餘路由都未能匹配請求 URL 時所執行的路由。一般,未處理請求會經過 Laravel 的異常處理器自動渲染一個「404」頁面,不過,若是你在 routes/web.php
文件中定義了 fallback
路由的話,全部 web
中間件組中的路由都會應用此路由做爲兜底,固然,若是須要的話,你還能夠添加額外的中間件到此路由:
Route::fallback(function () { return '您所請求的頁面不存在,請檢查'; });
注:兜底路由應該老是放到應用註冊的全部路由的最後。
Laravel 自帶了一箇中間件用於限制對應用路由的訪問頻率。開始使用該功能以前,分配 throttle
中間件到某個路由或路由分組,throttle
中間件接收兩個參數用於判斷給定時間內(單位:分鐘)的最大請求次數。例如,咱們指定登陸用戶每分鐘只能訪問下面的分組路由 60 次:
Route::middleware('auth:api', 'throttle:60,1')->group(function () { Route::get('/user', function () { // }); });
超出訪問次數後,會返回 429
狀態碼並提示「Too many requests」。
動態頻率限制
此外,還能夠基於 User
模型的屬性來動態設置最大請求次數。
例如,若是 User
模型包含 rate_limit
屬性,就能夠將其這個屬性名傳遞到 throttle
中間件,這樣就能夠將屬性值做爲計算最大請求次數的數據來源:
Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () { Route::get('/user', function () { // }); });
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>
#還能夠直接使用 Blade 指令 來生成 字段:@method_method
<form action="/foo/bar" method="POST"> @method('PUT') @csrf </form>
你可使用 Route
門面上的 current
、currentRouteName
和 currentRouteAction
方法來訪問處理當前輸入請求的路由信息:
// 獲取當前路由實例 $route = Route::current(); // 獲取當前路由名稱 $name = Route::currentRouteName(); // 獲取當前路由action屬性 $action = Route::currentRouteAction();
---恢復內容結束---