Laravel路由學習筆記

路由

本篇主要講解在Laravel框架中路由的基本知識,主要參考中文文檔中的路由章節,並融合本身的理解。以通俗易懂的方式來解析Laravel框架中最重要的路由知識。php


1.前提知識

學習Laravel路由以前咱們好好了解如下幾個知識
第一個就是 —— HTTP 協議基礎。不要求徹底瞭解 HTTP 協議,但必需要知道一小部分概念。若是對其不瞭解,會在閱讀 laravel 文檔時出現不少疑惑。對於HTTP協議的理解能夠參考這篇文章深刻理解HTTP協議html

第二個是 —— RESTful,這是一種設計思想,這對學習 laravel 的路由有很大的幫助,尤爲是在學習 laravel 控制器的資源控制器部分,不少人的疑惑都是在不瞭解 RESTful 的狀況下產生的。阮一峯的日誌中有兩篇詳細講解了 RESTful,其中一篇是理解RESTful架構laravel

2.路由的學習

①路由的定義文件

全部的 Laravel 路由都在 routes 目錄中的路由文件中定義,這些文件都由框架自動加載。routes/web.php 文件用於定義 web 界面的路由。這裏面的路由都會被分配給 web 中間件組,它提供了會話狀態和 CSRF 保護等功能。定義在 routes/api.php 中的路由都是無狀態的,而且被分配了 api 中間件組。web

大多數的應用構建,都是以在 routes/web.php 文件定義路由開始的。能夠經過在瀏覽器中輸入定義的路由 URL 來訪問 routes/web.php 中定義的路由。例如,你能夠在瀏覽器中輸入 http://your-app.dev/user 來訪問如下路由正則表達式

Route::get('/user', 'UserController@index');

②基本路由

構建基本的路由只須要一個 URI 與一個 閉包函數(什麼是閉包函數,想了解更多閉包函數,請看這篇PHP的閉包) ,這裏提供了一個很是簡單優雅定義路由的方法:數據庫

Route::get('foo', function () {
    return 'Hello World';
});

③可用的路由方法

路由器容許你註冊能響應任何 HTTP 請求的路由,大體有如下七種api

Route::get('books', 'BooksController@index');           //獲取
Route::post('books', 'BookController@store');           //保存
Route::get('books/create', 'BookController@create');    //建立 
Route::get('books/:id', 'BookController@show');         //顯示
Route::put('books/:id', 'BookController@update');       //更新
Route::delete('books/:id', 'BookController@destroy');   //刪除
Route::get('books/:id/edit', 'BookController@edit');    //編輯

以上的七種方法能夠用Route:source的方法來進行總結使用。後面會對Route:source方法進行詳解。數組

Route::resource('books', 'BooksController');

有的時候你可能須要註冊一個可響應多個 HTTP 請求的路由,這時你可使用 match 方法,也可使用 any 方法註冊一個實現響應全部 HTTP 請求的路由,可是根據面向對象的單一職責原則,一個類或者一個模塊應該有且只有一個職責,因此不推薦使用。(什麼是單一職責原則?請看這篇文章單一職責原則)瀏覽器

Route::match(['get', 'post'], '/', function () {
    //
});

Route::any('foo', function () {
    //
});

④重定向路由

若是要定義重定向到另外一個 URI 的路由,可使用 Route::redirect 方法。這個方法能夠快速的實現重定向,而再也不須要去定義完整的路由或者控制器:restful

Route::redirect('/here', '/there');

Route::redirect 默認會返回狀態碼 302 。 你能夠經過第三個參數自定義返回碼:

Route::redirect('/here', '/there', 301);

你也可使用 Route::permanentRedirect 方法來返回 301 狀態碼:

Route::permanentRedirect('/here', '/there');

⑤視圖路由

若是你的路由只須要返回一個視圖,可使用 Route::view 方法。它和 redirect 同樣方便,不須要定義完整的路由或控制器。
view 方法有三個參數,其中第一個是必填參數,是包含視圖名稱的 URI 。第二個也是必填參數,是須要渲染的視圖名稱。第三個參數是可選參數,能夠傳入一個數組,數組中的數據會被傳遞給視圖:

Route::view('/welcome',  'welcome');      //參數1:URL 參數2:視圖名稱 

Route::view('/welcome', 'welcome', ['name' => 'Taylor']); //參數1:URL 參數2:視圖名稱  參數3:可選參數(能夠傳數組)

⑥路由參數

1>必填參數

固然,有時須要在路由中捕獲一些 URL 片斷。例如,從 URL 中捕獲用戶的 ID,能夠經過定義路由參數來執行此操做:

Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});

也能夠根據須要在路由中定義多個參數:

Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //
});

路由的參數一般都會被放在 {} 內,而且參數名只能爲字母,同時路由參數不能包含-符號,若是須要能夠用下劃線 (_) 代替。路由參數會按順序依次被注入到路由回調或者控制器中,而不受回調或者控制器的參數名稱的影響。

2>可選參數

有時,你可能須要指定一個路由參數,但你但願這個參數是可選的。你能夠在參數後面加上 ? 標記來實現,但前提是要確保路由的相應變量有默認值

Route::get('user/{name?}', function ($name = null) {
    return $name;
});

Route::get('user/{name?}', function ($name = 'John') {
    return $name;
});
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]+']);
4>全局約束

若是你但願某個具體的路由參數都遵循同一個正則表達式的約束,就使用 pattern 方法在 appProvidersRouteServiceProvider 的 boot 方法中定義這些模式:

/**
 * 定義你的路由模型綁定, pattern 過濾器等。
 *
 * @return void
 */
public function boot()
{
    Route::pattern('id', '[0-9]+');

    parent::boot();
}

一旦定義好以後,便會自動應用這些規則到全部使用該參數名稱的路由上:

Route::get('user/{id}', function ($id) {
    // 只有在 id 爲數字時才執行。
});

⑦路由命名

路由命名能夠方便地爲指定路由生成 URL 或者重定向。經過在路由定義上鍊式調用 name 方法能夠指定路由名稱:

Route::get('user/profile', function () {
    //
})->name('profile');

你還能夠指定控制器行爲的路由名稱:

Route::get('user/profile', 'UserProfileController@show')->name('profile');

生成指定路由的 URL
爲路由指定了名稱後,就可使用全局輔助函數 route 來生成連接或者重定向到該路由:

// 生成 URL...
$url = route('profile');

// 生成重定向...
return redirect()->route('profile');

若是是有定義參數的命名路由,能夠把參數做爲 route 函數的第二個參數傳入,指定的參數將會自動插入到 URL 中對應的位置:

Route::get('user/{id}/profile', function ($id) {
    //
})->name('profile');

$url = route('profile', ['id' => 1]);

檢查當前路由
若是你想判斷當前請求是否指向了某個路由,你能夠調用路由實例上的 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 方法的第一個參數中。

1>路由組中共享中間件

要給路由組中全部的路由分配中間件,能夠在 group 以前調用 middleware 方法,中間件會依照它們在數組中列出的順序來運行。

Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // // 使用 first 和 second 中間件
    });

    Route::get('user/profile', function () {
        // // 使用 first 和 second 中間件
    });
});
2>路由組中共享命名空間
另外一個常見用例是使用 namespace 方法將相同的 PHP 命名空間分配給路由組的中全部的控制器。

Route::namespace('Admin')->group(function () {
    // 在 "App\Http\Controllers\Admin" 命名空間下的控制器
});

請記住,默認狀況下,RouteServiceProvider 會在命名空間組中引入你的路由文件,讓你不用指定完整的 AppHttpControllers 命名空間前綴就能註冊控制器路由。所以,你只須要指定命名空間 AppHttpControllers 以後的部分。

3>路由組中共享子域名

路由組也能夠用來處理子域名。子域名能夠像路由 URI 同樣被分配路由參數,容許你獲取一部分子域名做爲參數給路由或控制器使用。能夠在 group 以前調用 domain 方法來指定子域名。(什麼是子域名)

Route::domain('{account}.myapp.com')->group(function () {
    Route::get('user/{id}', function ($account, $id) {
        //
    });
});
3>路由組中共享路由前綴

能夠用 prefix 方法爲路由組中給定的 URL 增長前綴。例如,你能夠爲組中全部路由的 URI 加上 admin 前綴。

Route::prefix('admin')->group(function () {
    Route::get('users', function () {
        // 匹配包含 "/admin/users" 的 URL
    });
});
4>路由組中共享路由名稱前綴

name 方法能夠用來給路由組中的每一個路由名稱添加一個給定的字符串。 例如,您可能但願以 「admin」爲全部分組路由的名稱加前綴。 給定的字符串與指定的路由名稱前綴徹底相同,所以咱們將確保在前綴中提供尾部的 . 字符.

Route::name('admin.')->group(function () {
    Route::get('users', function () {
        // 指定路由名爲 "admin.users"...
    })->name('users');
});

⑨路由模型綁定

當向路由或控制器行爲注入模型 ID 時,就須要查詢這個 ID 對應的模型。Laravel 爲路由模型綁定提供了一個直接自動將模型實例注入到路由中的方法。例如,你能夠注入與給定 ID 匹配的整個 User 模型實例,而不是注入用戶的 ID。

1>隱式綁定

Laravel 會自動解析定義在路由或控制器行爲中與類型提示的變量名匹配的路由段名稱的 Eloquent 模型。例如:

Route::get('api/users/{user}', function (App\User $user) {
    return $user->email;
});

在這個例子中,因爲 $user 變量被類型提示爲 Eloquent 模型 AppUser,變量名稱又與 URI 中的 {user} 匹配,所以,Laravel 會自動注入與請求 URI 中傳入的 ID 匹配的用戶模型實例。若是在數據庫中找不到對應的模型實例,將會自動生成 404 異常。

2>自定義鍵名

若是你想要模型綁定在檢索給定的模型類時使用除 id 以外的數據庫字段,你能夠在 Eloquent 模型上重寫 getRouteKeyName 方法:

/**
 * 獲取該模型的路由的自定義鍵名。
 *
 * @return string
 */
public function getRouteKeyName()
{
    return 'slug';
}
2>顯式綁定

要註冊顯式綁定,使用路由器的 model 方法來爲給定參數指定類。在 RouteServiceProvider 類中的 boot 方法內定義這些顯式模型綁定。

public function boot()
{
    parent::boot();

    Route::model('user', App\User::class);
}

接着,定義一個包含 {user} 參數的路由:

Route::get('profile/{user}', function (App\User $user) {
    //
});

由於咱們已經將全部 {user} 參數綁定至 AppUser 模型,因此 User 實例將被注入該路由。例如,profile/1 的請求會注入數據庫中 ID 爲 1 的 User 實例。若是在數據庫中找不到匹配的模型實例,就會自動拋出一個 404 異常。

2>自定義邏輯解析

若是你想要使用自定義的解析邏輯,就使用 Route::bind 方法。傳遞到 bind 方法的 閉包 會接受 URI 中大括號對應的值,而且返回你想要在該路由中注入的類的實例:

/**
 * 啓動應用服務。
 *
 * @return void
 */
public function boot()
{
        parent::boot();

        Route::bind('user', function ($value) {
                return App\User::where('name', $value)->first() ?? abort(404);
        });
}

或者,您能夠重寫 Eloquent 模型上的 resolveRouteBinding 方法。 此方法會接受 URI 中大括號對應的值,而且返回你想要在該路由中注入的類的實例。

/**
 * 檢索綁定值的模型。
 *
 * @param  mixed  $value
 * @return \Illuminate\Database\Eloquent\Model|null
 */
public function resolveRouteBinding($value)
{
    return $this->where('name', $value)->first() ?? abort(404);
}
2>回退路由

使用 Route::fallback 方法, 你能夠定義在沒有其餘路由匹配傳入請求時執行的路由。一般,未處理的請求會經過應用程序的異常處理程序自動呈現 「404」 頁面。 可是,由於你能夠在 routes/web.php 文件中定義 fallback 路由,web 中間件的全部中間件都將應用到路由中。 固然,你也能夠根據須要向這條路由中添加額外的中間件:

Route::fallback(function () {
    //
});

注意:回退路由應始終是你應用程序註冊的最後一個路由。

⑩訪問控制

Laravel 包含了一個 middleware 用於控制應用程序對路由的訪問。 若是想要使用,請將 throttle 中間件分配給一個路由或者一個路由組。throttle 中間件會接收兩個參數,這兩個參數決定了在給定的分鐘數內能夠進行的最大請求數。例如,讓咱們指定一個通過身份驗證而且用戶每分鐘訪問頻率不超過 60 次的路由組。

Route::middleware('auth:api', 'throttle:60,1')->group(function () {
    Route::get('/user', function () {
        //
    });
});
1>動態訪問控制

你能夠根據已驗證的 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 輸入標籤。使用 _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 模板指令生成 _method 輸入:

<form action="/foo/bar" method="POST">
    @method('PUT')
    @csrf
</form>

CSRF 保護

指向 web 路由文件中定義的 POST、PUT 或 DELETE 路由的任何 HTML 表單都應該包含一個 CSRF 令牌字段,不然,這個請求將會被拒絕。能夠在 CSRF 文檔 中閱讀有關 CSRF 更多的信息.

<form method="POST" action="/profile">
    @csrf
    ...
</form>

⑫訪問當前路由

你可使用 Route facade 上的 current 、 currentRouteName 和 currentRouteAction 方法來訪問處理傳入請求的路由的信息。

$route = Route::current();

$name = Route::currentRouteName();

$action = Route::currentRouteAction();
相關文章
相關標籤/搜索