任何Web應用框架必要的功能就是獲取用戶的請求並返回一個響應, 通常是經過HTTP(S)。也就意味着當學習一個Web框架的時候,定義一個應用的路由是第一位的,同時也是要去解決的最重要的部分,沒有路由,你是無法和終端用戶交互的。javascript
在本文,咱們會學習Laravel的路由,看看如何定義它們,如何把它們指到要執行代碼的地方,怎麼樣使用Laravel的路由工具來處理不一樣的路由。php
在Laravel應用裏,Web 相關的路由定義在 routes/web.php
裏,API 相關的路由定義在 routes/api.php
裏。Web 路由會被終端用戶訪問到,而 API 路由主要是應用與API,好比你有一個移動客戶端。接下來的內容,咱們主要關注在web路由 routes/web.php
。html
若是Laravel版本早於5.3, 只有一個路由文件
app/Http/routes.php
前端
定義一個路由最簡單的方式就是匹配一個帶有閉包的根路徑(好比: /
),好比java
// routes/web.php Route::get('/', function() { return 'Hello, World!'; })
什麼是閉包? 閉包是PHP的匿名函數版本。更詳細的說閉包是一個能夠做爲對象傳遞,分配給變量,做爲參數傳遞給其餘函數和方法,甚至能夠序列化。web
如今你已經定義了一個路由,若是有人訪問 /
, Laravel 路由會執行定義好的閉包並返回結果。 注意這裏是 return
內容而不是 echo
或 print
。ajax
快速瞭解中間件 你可能想要知道,爲何是
return
Hello World!
代替echo
。 這裏也有很多答案,可是最簡單的是 Laravel 的請求和響應週期有不少包裝,包括中間件。當路由閉包或者控制器方法執行完的時候,尚未時間將輸出發送到瀏覽器;返回內容容許它在返回給用戶以前繼續流過響應堆棧和中間件。正則表達式
許多簡單的網站能夠徹底定義在web路由文件裏。一些簡單的 GET 路由也能夠和模板組合着用,你能夠很容易的啓動一個網站,好比shell
Route::get('/', function () { return view('welcome'); }); Route::get('about', function () { return view('about'); }); Route::get('products', function () { return view('products'); }); Route::get('services', function () { return view('services'); });
關於靜態調用 若是你對PHP開發有一些經驗,你看到對於路由類的靜態調用可能有一些驚訝。實際上這不是一個靜態方法,而是使用了Laravel的
facade
, 後面會有文章專門來介紹。 若是你不喜歡facade, 你也能夠完成相同的定義,如數據庫
$router->get('/', function () { return 'Hello, World!'; });
你可能注意到咱們已經在路由定義裏使用過 Route::get
。這意思就是咱們告訴 Laravel 當有一個HTTP請求使用 GET 時只能匹配到這條路由。那若是是form的 POST, 或者是JavaScript發送了 PUT 或者 DELETE 請求呢? 下面有一些在路由定義時能夠用的其餘選項。
Route::get('/', function(){ return 'Hello, World!'; }); Route::post('/', function(){}); Route::put('/', function(){}); Route::delete('/', function () {}); Route::any('/', function () {}); Route::match(['get', 'post'], '/', function () {});
正如你可能已經猜到的那樣,將閉包傳遞給路由定義並非教導它如何解決路由的惟一方法。閉包是快速且簡單,可是應用程序越大,將全部路由邏輯放在一個文件中變得越笨拙。另外,使用路由閉包的應用也不能使用 Laravel 路由緩存(使用路由緩存能夠爲每一個請求減小几百毫秒)。
另外一個經常使用的選項是傳遞一個控制器和方法名做爲一個字符串代替閉包,例如
Route::get('/', 'WelcomeController@index');
該命令是告訴 Laravel 把請求傳給 App\Http\Controllers\WelcomeController
控制器的 index()
方法。並且也能夠傳遞與閉包相同的參數。
若是你定義的路由有參數,在路由中定義它們並把它們傳遞給閉包也是很是簡單的。
Route::get('users/{id}/friends', function($id) { // your code });
關於 路由參數和閉包/控制器裏方法參數之間的命名關係 這裏要多說一下,可能不少人對這裏都會有些疑問。 如你看到上面這個路由參數的例子,它是很是常見的,用於路由參數(
{id}
)和注入到路由定義裏(function($id)
)的方法參數使用相同的參數名。可是這有必要這麼作麼? 除非你使用路由/模型綁定, 不然是沒有必要的。定義的路由參數匹配哪個方法參數惟一要注意的是他們的順序(從左到右),看下面的例子
Route::get('users/{userId}/comments/{commentId}', function ( $thisIsActuallyTheUserId, $thisisReallyTheCommentId ){ // });
我推薦它們保持一致,經過命名相同能夠減小錯誤。
經過在參數名後面使用?
也可讓路由參數變爲可選。看下面的代碼,這個case中,給參數提供了一個默認值。
Route::get('users/{id?}', function ($id = 'fallbackId') { // });
你也可使用正則表達式來定義一個路由,該路由只有參數知足實際的要求才會匹配,如
Route::get('users/{id}', function ($id) { // })->where('id', '[0-9]+'); Route::get('users/{username}', function ($username) { // })->where('username', '[A-Za-z]+'); Route::get('posts/{id}/{slug}', function ($id, $slug) { // })->where(['id' => '[0-9]+', 'slug' => '[A-Za-z]+']);
若是你訪問的路徑匹配到了一個路由,可是正則表達式不匹配參數,那最終也不會匹配到。因爲路由匹配是從上到下的,因此 /users/abc
會跳過第一個路由,可是會匹配到第二條閉包路由。另外一方面 posts/abc/123
不會匹配到任何一條路由,因此會返回 404 Not Found
的錯誤。
在應用中使用這些路由最簡單的方式就是隻使用他們的路徑(path
)。url()
helper 能夠簡化視圖裏的連接。看下面的例子,helper 會給路由前面加上網站的完整域名。
<a href="<?php echo url('/'); ?>"> // outputs <a href="http://myapp.com/">
並且Laravel還容許你給每一個路由起一個名字,使你能夠在不明確引用URL的狀況下使用它。這頗有幫助,由於這意味着你能夠給複雜的路由提供簡單的暱稱,而且由於按名稱連接它們意味着若是路徑更改,則沒必要重寫前端連接。
// Defining a route with name in routes/web.php: Route::get('members/{id}', 'MembersController@show')->name('members.show'); // Link the route in a view using the route() helper <a href="<?php echo route('members.show', ['id' => 14]); ?>">
這個例子也提到了幾個新的概念。首先是能夠經過在 get()
方法以後經過鏈式方法 name()
使用熟練的路由定義來添加名稱。而後這個方法容許咱們指定一個簡短的別名,來在其餘地方更容易使用。
咱們命名路由爲 members.show
, 資源複數 在Laravel的路由和視圖名也是經常使用的規範。
- 關於路由命名規範 * 你能夠命名路由按照你喜歡的方式,可是經常使用到的規範是使用資源名稱複數,而後使用一個點
.
, 而後是具體的動做。這裏有一組給圖片資源命名的最經常使用到的路由名稱:photos.index photos.create photos.store photos.show photos.edit photos.update photos.destroy
咱們也介紹過 route()
helper, 就像 url()
同樣,它的目的是在視圖中使用,以簡化與命名路由的連接。若是路由裏沒有參數,你能夠簡單的傳一個路由名 route('members.index')
,轉化後的結果爲: http://myapp.com/members/index
。若是有參數,以數組的形式傳入第二個參數裏。
通常狀況下,我推薦使用路由暱稱來代替使用路由路徑,所以使用 route()
代替 url()
。
- 關於傳遞路由參數到route() *
當路由有參數的時候(好比:
users/{id}
), 在使用route()
生成連接給路由的時須要定義這些參數。傳遞參數有幾種不一樣的方式,設想一個路由是
users/{userId}/comments/{commentId}
。 若是用戶ID是1,評論ID是2,來看下咱們可使用的幾種方式:方式1:
route('users.comments.show', [1, 2]) // http://myapp.com/users/1/comments/2方式2:
route('users.comments.show', ['userId' => 1, 'commentId' => 2]) // http://myapp.com/users/1/comments/2方式3:
route('users.comments.show', ['commentId' => 2, 'userId' => 1]) // http://myapp.com/users/1/comments/2方式4:
route('users.comments.show', ['userId' => 1, 'commentId' => 2, 'opt' => 'a']) // http://myapp.com/users/1/comments/2?opt=a如你所見,沒有key的數組是須要按順序指定的,帶有key的數組經過和路由裏的key進行匹配,剩下的會被做爲查詢參數。
一般,一組路由共享一個特定的特徵 - 特定的身份驗證要求,路徑前綴或控制器命名空間。在每一條路由裏一遍又一遍的定義這些共享的特徵不只看起來乏味,並且可能會玷污你的路由文件,而且掩蓋應用程序的某些結構。
路由組容許將幾個路由組合到一塊兒,應用共享的配置到整個路由組一次,減小重複。另外,路由組也是給將來開發者的一個能夠看的見的線索。
要講兩個或更多個路由組合到一塊兒,你要圍繞一組路由。以下
定義一組路由
Route::group([], function(){ Route::get('hello', function() { return 'Hello'; }); Route::get('world', function() { return 'world'; }) });
默認狀況下,路由組實際上不會作任何事情。上面的代碼和以前的路由基本沒啥區別。其中第一個參數是一個空數組,容許傳入多重配置參數去應用到整個路由組。
可能對路由組最經常使用的就是應用一箇中間件到一組路由組。後面會專門寫一片關於中間件的文章。除此以外,它們是Laravel用於驗證用戶身份並限制訪客使用網站的某些部分的內容。
下面的例子,是咱們建立了一個路由組,包含 dashboard
和 account
視圖,應用 auth
中間件到這兩個路由。在這個例子中也就意味着用戶必須登陸到應用裏來看 dashboard
和 account
頁。
只有登陸用戶能夠訪問的路由組
Route::group(['middleware' => 'auth'], function(){ Route::get('dashboard', function(){ return view('dashboard'); }); Route::get('account', function(){ return view('account'); }) });
關於在控制器中使用中間件
常常在控制器中使用路由中間件比在路由定義裏可能更清晰,更直接。你能夠在控制器的構造方法裏使用 middleware()
方法來作到。給 middleware()
傳遞的參數就是中間件的名稱。你也能夠鏈式調用一些方法(好比: only()
, except()
) 來定義哪些方法將接受該中間件。
class DashboardController extends Controller { public function __construct() { $this->middleware('auth'); $this->middleware('admin-auth') ->only('admin'); $this->middleware('team-member') ->except('admin'); } }
請注意,若是你大量的使用 only
和 except
, 之後會很難維護,因此不建議大面積這麼使用。
若是你有一組路由要共享一個path, 好比你網站的API是以 /api
開頭的,那就可使用路由組來指定這個結構,如
前綴路由組
Route::group(['prefix' =>'api'], function(){ Route::get('/', function(){ // handle the path /api }); Route::get('users', function(){ // handle the path /api/users }); });
注意每個前綴路由組都會有一個
/
路由來表示根前綴,在這裏就是/api
子域名路由和路由前綴同樣,可是它的做用域是子域名而不是路由前綴。有兩種主要的使用方法。
第一種,你可讓整個應用分爲不一樣的幾個部分(或不一樣的應用)到不一樣的子域名,來看下怎麼實現
子域名路由
Route::group(['domain' => 'api.myapp.com'], function(){ Route::get('/', function(){ // }); });
第二種,你也能夠將子域名的一部分做爲參數,最多見的例子就是slack,好比每個公司都有本身的子域名 phpcasts.slack.co
。
參數化的子域名
Route::get(['domain' => '{account}.myapp.com'], function(){ Route::get('/', function($account) { // }); Route::get('users/{id}', function($account, $Id){ // }); });
注意,路由組裏的任何一個參數均可以傳入到組裏的路由方法裏做爲第一個參數,若是是多個就依次寫。
當你按子域名或路由前綴對路由進行分組時,極可能他們的控制器有一個相似PHP命名空間。在API的例子裏,全部的API路由控制器可能都在 API
命名空間。 經過使用路由組命名空間前綴,能夠避免在諸如 API/ControllerA@index
和 API/ControllerB@index
之類的組中使用較長的控制器引用。
路由組命名空間前綴
// App\Http\Controllers\ControllerA Route::get('/', 'ControllerA@index'); Route::group(['namespace' => 'API'], function () { // App\Http\Controllers\API\ControllerB Route::get('api/', 'ControllerB@index'); });
前綴並不止於此。一般路由名稱會反映路徑元素的繼承鏈,所以 users/comments/5
將由名爲 users.comments.show
的路由提供服務。 在這種狀況下, 一般在 users.comments
資源下的全部路由上層使用路由組。
路由名稱前綴
Route::group(['as' => 'users.', 'prefix' => 'users'], function () { Route::group(['as' => 'comments.', 'prefix' => 'comments'], function () { // Route name will be users.comments.show Route::get('{id}', function () { // })->name('show'); }); });
這部分先簡單瞭解下就能夠了,後面會有專門一文來講模板視圖。
目前爲止咱們看到的路由閉包裏,使用過一行 view('account')
。這裏是發生了什麼呢?
若是你不熟悉 Model-View-Controlelr(MVC)模式, views
(或者是模板) 是描述一些實際輸出的文件。你也可能用於 JSON
或者 XML
或者 郵件 的視圖,可是在Web框架裏最經常使用的視圖是輸出 HTML。
在Laravel, 有兩種能夠當即使用的視圖格式:plain php
和 Blade
模板。不一樣的是在於文件名:about.php
會被PHP引擎來渲染, about.blade.php
則會被 Blade
引擎渲染。
關於加載模板視圖的三種方式
有三種不一樣的方式來返回視圖。到目前,只是控制器本身使用過
view()
, 但你曾可能看到過View::make()
的話,它和view()
作的事是同樣的,還可使用注入的方式Illuminate\View\ViewFactory
。
view()的簡單使用
Route::get('/', function () { return view('home'); });
這段代碼將會尋找 resources/views/home.blade.php
或 resources/views/home.php
視圖文件,並加載裏面的內容,解析PHP代碼 或 控制結構語句知道視圖輸出。一旦返回以後,被專遞到響應棧的剩餘部分,最終返回給用戶。
可是若是你須要傳遞變量呢? 來看下面的例子
傳遞變量到模板
Route::get('tasks', function(){ return view('tasks.index') ->with('tasks', Task::all()); });
有時候一遍又一遍的傳遞一樣的變量到模板會感受很麻煩。這個變量多是網站的每一個視圖都要訪問的,或者是某一類視圖,再或者是一些子視圖,好比 全部的視圖都會用到的 header 。
視圖共享變量
view()->share('variableName', 'variableValue');
我已經好幾回提到過控制器,但直到如今大多數的例子中只展現了路由閉包。若是不熟悉 MVC 模式(看下圖),控制器本質上是一個類,它將一條或多條路由的邏輯組織在一塊兒。控制器傾向與將相似的路由組合在一塊兒,特別是若是你的應用是按照傳統的相似CRUD的格式構建的;在這種狀況下,控制器可能會處理能夠在特定資源上執行的全部操做。
關於什麼是CRUD
CRUD 表明建立、讀取、更新、刪除,這是Web應用程序最常提供的四項主要操做。例如,你能夠建立新的博客文章,能夠閱讀該文章,能夠更新它,也能夠將其刪除。
將全部應用程序的邏輯嵌入到控制其中可能很誘人,但最好將控制器看作是將HTTP請求路由到應用程序的交通警察。因爲還有其餘的方式能夠將請求加入到你的應用程序中,例如 cron
做業,Artisan
命令行調用,隊列做業等等,因此不要依賴控制器來處理太多行爲。這意味着控制器的主要工做是捕獲HTTP請求的意圖並將其傳遞給應用程序的其他部分。
來建立一個控制器,最簡單的方式就是經過 Artisan 命令,在命令裏運行以下命令
php srtisan make:controller TasksController
關於Artisan和Artisan生成器 Laravel 自帶了一個名爲Artisan的命令行工具。Artisan 可用於運行遷移,手動建立用戶和其餘數據庫記錄,一次性任務等等。 在 make 命令下,Artisan提供了用於爲各類系統文件生成骨架文件的工具。 後面會專門文章來介紹 Artisan。
這條命令會在 app/Http/Controllers
目錄下建立一個名爲 TasksController.php
的新文件,同時包含一下內容
默認生成的控制器
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; class TasksController extends Controller { }
修改這個文件而後增長一個新的方法 home()
, 返回一些簡單的文本
簡單的控制器
<?php use App\Http\Controllers\Controller; class TasksController extends Controller { public function home() { return 'Hello, World!'; } }
這個以前有學過,來看下如何把一個路由綁定到這個控制器上。
路由和控制器綁定
<?php Route::get('/', 'TasksController@home');
這樣就能夠經過訪問 /
路由看到返回的內容了 Hello, World!
。
關於控制器的命名空間 在上面的例子中看到,咱們引用了一個徹底限定類名的控制器
App\Http\Controllers\TasksController
, 但只使用了類名。 這不是由於咱們簡單地經過它們的類名來引用控制器,相反,當咱們引用控制器時,咱們忽略了App\Http\Controllers\
; 默認狀況 下,Laravel 配置爲在該命名空間下查找控制器。
控制器方法最經常使用的用法以下
經常使用的控制器方法
// TasksController.php ... public function index() { return view('tasks.index') ->with('tasks', Task::all()); }
這個控制器方法加載了 resources/views/tasks/index.blade.php
or resources/views/tasks/index.php
視圖,並傳遞了一個名爲 tasks
的變量,其值是一個 Task::all()
的查詢結果。
生成資源控制器 若是你曾在Laravel 5.3以前使用過
php artisan make:controller
,你可能指望它來自動生成全部基本的資源路由如create()
和update()
。在5.3以後須要使用--resource
參數來生成,如php artisan make:controller TasksController --resource
。
在控制器方法裏第二個最經常使用的動做就是獲取用戶輸入的參數並使用它。這裏引入了一些新的概念,來看下代碼
基本的動做綁定
// routes/web.php Route::get('tasks/create', 'TasksController@create'); Route::post('tasks', 'TasksController@store');
注意這裏咱們綁定了 GET
動做到 tasks/create
(顯示錶單), POST
動做綁定到了 tasks/
(當建立任務的時候POST數據)。咱們假設 create()
方法只顯示錶一個form表單,因此來看下 store()
方法。
通用表單輸入控制器的方法
// TasksController.php ... public function store() { $task = new Task; $task->title = Input::get('title'); $task->description = Input::get('description'); $task->save(); return redirect('tasks'); }
這個例子中使用了 Eloquent
model 和 redirect()
函數,後面會詳細介紹,並且你也能夠看到它實際上作了什麼。咱們建立了一個新 Task
, 獲取用戶輸入的數據,設置 task model, 保存,最後跳轉會任務列表頁。
有兩種方式能夠獲取用戶輸入的數據: facade
方式和 Request
對象(這個後面新開文章來寫)。
關於導入facade 運行上面的代碼你可能發現會報
Input
facade找不到,這是由於它們(facade)不會出如今每個namespace
下。但在根命名空間下是有效的。因此咱們須要在文件頂部導入Input
facade。有兩種方式導入:\Input
或Illuminate\Support\Facades\Input
。如<?php namespace App\Http\Controllers; use Illuminate\Support\facades\Input; // or \Input class TasksController { public function store() { $task = new Task; $task->title = Input::get('title'); $task->description = Input::get('description'); $task->save(); return redirect('tasks'); } }
Laravel的 facade
爲Laravel的代碼庫中最有用的類提供了一個簡單的接口。經過使用簡單的 facade
能夠獲取關於當前請求,用戶輸入,session, cache 的信息等。
但若是你更喜歡注入依賴或者使用一個service, 你須要找到一些傳入這些類實例到控制的方法。
這也是咱們要將的,關於Laravel的服務容器(service container)。到目前,若是不熟悉這點,你能夠認爲這是Laravel的一點小魔法,關於容器更多的內容後面章節會有講解。
全部控制器方法(包括構造行數)都從 Laravel 的容器中解析出來, 這意味着你鍵入的任何內容容器都知道如何解決都會被自動注入。 下面有一個很好的例子,若是你但願有一個 Request
對象的實例, 而不是使用 facade
, 那該怎麼辦? 能夠僅僅鍵入 Illuminate\Http\Request
在方法參數裏,如
控制器方法注入
// TasksController.php ... public function store(\Illuminate\Http\Request $request) { $task = new Task; $task->title = $request->input('title'); $task->description = $request->input('description'); $task->save(); return redirect('tasks'); }
順便說一句,這其實是我和其餘許多Laravel開發人員更喜歡獲取用戶輸入的方式:注入 Request
的實例並從那裏讀取用戶輸入,而不是依賴 Input
facade。
有時在控制器裏命名一個方法名多是寫一個控制器很是艱難的一部分。 值得慶幸的是,Laravel 對傳統 REST/CRUD 控制器(Laravel中稱爲資源控制器)的全部路由都有一些約定;此外,它附帶了一個開箱即用的生成器和一個便捷的路由定義,可以讓你一次綁定整個資源控制器。
想要看Laravle資源控制器提供的方法,咱們能夠經過命令行來生成一個
php artisan make:controller MySampleResourseController --resource
如今打開 app\Http\Controllers\MySampleResourceController.php
。你就能夠看到增長一些方法。讓咱們來看下每個表明什麼,經過 Task
例子來理解。
經過這個表格來看下
|動做|URL|控制器方法名|路由名|描述| |:--|:--|:--|:--|:--| |GET|tasks|index()|tasks.index| 展現全部的task| |GET|tasks/create|create()|tasks.create|展現建立task的表單form| |POST|tasks|store()|tasks.store|接收來自建立task表單的提交| |GET|tasks{/task}|show()|tasks.show|展現一條task| |GET|tasks/{task}/edit|edit()|tasks.edit|編輯一條task| |PUT/PATCH|tasks/{task}|update()|tasks.update|接受來自編輯表單的提交| |DELETE|tasks/{task}|destory()|tasks.destory|刪除一條task|
能夠看到每個HTTP動做都有一個 URL, 控制器方法名,路由名稱和描述。
如咱們看到的,這些是在 Laravel中使用的 傳統路由名稱,而且很容易爲每一個這些默認路由生成一個資源控制器和方法。幸運的是,你沒必要手動爲每個控制器方法生成路由。代替的是,這裏有一個叫作資源綁定的動動。
資源控制器綁定
// routes/web.php Route::resource('tasks', 'TasksController');
該命令會自動綁定全部路由到指定控制器上適合的方法名。它也會適當的命名這些路由;例如 tasks
資源控制器裏的 index()
方法將會被命名爲 tasks.index
最經常使用的路由模式之一就是在任何控制器方法的第一行就去經過給定的ID查找資源,好比
獲取資源
Route::get('conferences/{id}', function($id) { $conference = Conference::findOrFail($id); });
Laravel提供了一種簡化稱爲「路由模型綁定」的模式的功能。
有兩種路由模型綁定:隱式和自定義(或顯式)
使用路由模型綁定最簡單的方式是爲該模型指定惟一的路由參數(例如:將其命名爲 $conference
而不是 $id
), 而後在閉包/控制器的方法裏鍵入這個參數,並使用同名的變量就能夠了。如
使用隱式的路由模型綁定
Route::get('conferences/{conference}', function (Conference $conference) { return view('conferences.show')->with('conference', $conference); });
因爲路由參數({conference}
)和方法參數名($conference)同樣, 方法參數鍵入時帶有 Conference
model(Conference $conference), Laravel看到這個會做爲一個路由模型綁定。每次這個路由被訪問到的時候,應用都會假設不管傳入什麼到URL,都會用 ID來替代 {conference}
,而後經過ID去到對應的 Conference
model裏查找。Conference
實例都會被轉入閉包或者控制器方法裏。
隱式路由模型綁定是在 Laravel 5.2版本中加入的,因此在 5.1 版本里是無法是無法使用的。
要手動的配置路由模型綁定,能夠添加 像下面的一行到文件 App\Providers\RouteServiceProvider
的 boot()
方法裏。
添加路由模型綁定
public function boot(Router $router) { parent::boot($router); // Perform the binding $router->model('event', Conference::class); }
如今你已經定義了每當路由的定義中有一個名爲 {event}
的參數時,路由解析器將返回具備該URL參數的ID的Conference類實例。
顯式路由模型綁定
Route::get('events/{event}', function (Conference $event) { return view('events.show')->with('event', $event); });
若是你但願縮短加載時間,那可能須要看下這個路由緩存。Laravel 的啓動過程之一是要解析 routes/*
, 可能會花費幾十毫秒到幾百毫秒,路由緩存能夠明顯加速這一過程。
要想緩存路由文件,你須要使用所有控制器或者資源控制器(而不是路由閉包)。若是你的應用沒有使用任何的路由閉包,能夠運行 php artisan route:cache
,Laravel會序列化路由文件 routes/*
結果。若是想要刪除緩存,能夠運行 php artisan route:clear
。
固然路由緩存也有缺點。當有緩存文件存在的時候,Laravel 只會匹配緩存的路由文件而不是實際的 routes/*
文件。你不管怎麼修改路由文件,都不會生效,直到你再次運行 php artisan route:cache
。這意味着每次進行更改時都必須從新緩存,這引發了很大的混淆。
這裏我會推薦這麼作:由於不管如何Git默認忽略路由緩存文件,因此只考慮在在生成環境服務器上使用路由緩存,而且每次部署新代碼時運行 php artisan route:cache
命令(不管是使用 Git post-hook, 仍是Forge部署),或做爲你使用的任何其餘部署系統的一部分。這樣你就不會混淆本地的開發問題,可是你的遠程環境任然受益於路由緩存。
有時,你須要手動定義一個form應該哪一個HTTP動詞,HTML表單僅支持 GET
或 POST
,因此若是想要其餘的動詞類型,須要本身來指定。
咱們已經討論了GET和POST HTTP動詞。若是對HTTP不熟悉,這裏簡單提一下。另外兩個最多見的是 PUT
和 DELETE
,但還有 HEAD
,OPTIONS
,PATCH
以及其餘兩種在正常Web開發中不多使用到的 TRACE
和 CONNECT
。
快速瞭解下這幾個動詞: GET
請求一個資源,HEAD
只請求 GET
中攜帶的header部分, POST
建立一個資源,PUT
重寫一個資源,PATCH
修改一個資源, DELETE
刪除一個資源,OPTIONS
是獲取這個URL哪些動詞是被容許使用的。
如咱們看到的,你能夠定義一個路由使用哪一個動詞來匹配路由定義中的 Route::get()
,Route::post()
,Route::any(), 或 Route::match()
。也能夠匹配 Route::patch()
, Route::put()
和 Route::delete()
。
但如何用瀏覽器發送一個除 GET
以外的請求呢? 首先,HTML表單中的 method
屬性決定了用哪一個HTTP動詞。若是表單有 GET
method
,則經過查詢參數和 GET
method
來提交,若是表單是 POST
則會經過 POST
method
和 post body 來提交。
JavaScript 框架發送其餘的請求很容易,像 DELETE
PATCH
。但你會發如今 Laravel 中須要提交一個HTML表單,除了 GET
,POST
, 須要使用表單方法欺騙,也就是在HTML表單中欺騙HTTP method
屬性。
要告訴Laravel你當前提交的表單應該被視爲POST以外的其餘內容,添加一個隱藏的變量名 _method
,其值能夠是 PUT
,PATCH
,DELETE
,Laravel 將匹配和路由該表單提交,就好像它實際上對該動詞的請求同樣。
看下面這個例子,由於它經過Laravel傳遞 DELETE
方法,將匹配 Route::delete()
定義的路由,但不會匹配 Route::post()
路由。
表單方法欺騙
<form action="/tasks/1" method="POST"> <input type="hidden" name="_method" value="DELETE"> </form>
若是你在Laravel應用中已經試着建立和提交一個表單,你可能會碰到這個異常 TokenMismatchException
。
默認狀況下,除了只讀路由(使用GET
,HEAD
或 OPTIONS
的路由)以外,Laravel中的全部路由都受到跨域請求僞造(CSRF)攻擊的保護,它須要一個以名爲 _token
的輸入形式的令牌, 與每一個請求一塊兒傳遞。該 token
會在每一個會話(session)開始時生成,而且每一個非只讀路由都會將提交的 _token
與會話令牌進行比較。
什麼是CSRF 跨網站請求僞造是指一個網站假裝成另外一個網站。 目標是經過登陸用戶的瀏覽器從他們的網站提交表單到您的網站,以便有人劫持您的用戶訪問您的網站。 CSRF攻擊的最佳方式是使用 Laravel 開箱即用的令牌來保護全部入站路由(POST,DELETE等)。
你有兩個選擇來解決這個問題。首先,首選的方法是將_token輸入添加到每一個提交中。 在HTML表單中,這很簡單,如
CSRF tokens
<form action="/tasks/5" method="POST"> <?php echo csrf_field(); ?> <!-- or: --> <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> </form>
在JavaScript應用程序中,可能有點多,但還好。 使用JavaScript框架的網站最多見的解決方案是將標記存儲在每一個頁面的 <meta>
標記中,以下所示:
<meta name="csrf-token" content="<?php echo csrf_token(); ?>" id="token">
將標記存儲在 <meta>
標記中能夠很容易地將其綁定到正確的HTTP header,對於來自JavaScript框架的全部請求,你能夠全局執行一次,以下所示。
把CSRF全局綁定到header
// in jQuery: $.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } }); // in Vue: Vue.http.interceptors.push((request, next) => { request.headers['X-CSRF-TOKEN'] = document.querySelector('#token').getAttribute('content'); next(); });
Laravel將根據每一個請求檢查 X-CSRF-TOKEN
,而且經過的有效令牌將標記CSRF保護爲滿意。 請注意,若是你使用5.3 Vue引導程序,則此示例中的CSRF的Vue語法不是必需的; 它已經爲你作了這項工做。
到目前爲止,咱們從控制器方法或路由定義返回的惟一東西是視圖。 可是咱們能夠返回一些其餘結構,以便瀏覽器指導如何操做。 首先,咱們介紹重定向。 有兩種常見的方法來生成重定向; 咱們將在這裏使用重定向全局helper,但你可能更喜歡facade。 二者都建立一個Illuminate\Http\RedirectResponse
的實例,對其執行一些便利方法,而後返回它。 你也能夠手動作到這一點,但你必須本身作更多的工做。 看下面的例子,看看幾種能夠返回重定向的方法。
返回重定向的幾種方式
// 使用全局helper生成 redirect response Route::get('redirect-with-helper', function () { return redirect()->to('login'); }); // 使用更簡短的全局helper Route::get('redirect-with-helper-shortcut', function () { return redirect('login'); }); // 使用facade 生成 redirect response Route::get('redirect-with-facade', function () { return Redirect::to('login'); });
請注意,redirect()
helper 與 Redirect
facade 是同樣的,但它也有一個快捷方式; 若是你將參數直接傳遞給 helper,而不是在它後面鏈式方法,則它是 to()
重定向方法的快捷方式。
先來看下參數說明
function to($to = null, $status = 302, $headers = [], $secure = null)
$to
有效的內部路徑$status
HTTP狀czzZ態碼(默認是 302 FOUND
)$headers
跳轉時攜帶的HTTP header$secure
是 http仍是httpsredirect()->to()
Route::get('redirect', function () { return redirect()->to('home'); // 簡短用法 return redirect('home'); });
route()
方法和 to()
方法相同,但不指向特定路徑,而是指向特定的路由名稱。
redirect()->route()
Route::get('redirect', function () { return redirect()->route('conferences.index'); });
請注意,因爲某些路由名稱須要參數,所以其參數順序稍有不一樣。route()
有第二個可選的參數做爲路由參數。
function route($to = null, $parameters = [], $status = 302, $headers = [])
帶參數的redirect()->route()
Route::get('redirect', function () { return redirect()->route('conferences.show', ['conference' => 99]); });
因爲Laravel會話實現的一些內置便利性,你的應用程序將始終知道用戶之前訪問的頁面是什麼。 這爲redirect()->back()
重定向提供了機會,它將用戶簡單地重定向到它來自的任何頁面。 還有一個全局可訪問的函數:back()
。
重定向服務提供了其餘不太經常使用的方法,這裏也說明下做用:
home()
重定向到一個名爲 home
的路由。refresh()
重定向到用戶當前所在的同一頁面。away()
容許在沒有默認網址驗證的狀況下重定向到外部網址。secure()
相似於 to()
,只是將安全參數設置爲 true
。action()
容許連接到控制器和方法,如 redirect()->action('MyController@myMethod')
。guest()
驗證用戶是否登陸當你將用戶重定向到不一樣的頁面時,你常常但願將某些數據與他們一塊兒傳遞。 能夠手動將數據閃存到會話中,但 Laravel 有一些便利方法能夠幫助你解決這個問題。
大多數狀況下,你能夠傳遞鍵值對的數組,或者使用 with()
來傳遞一個鍵和值,如
帶有數據重定向
// 單個的鍵和值 Route::get('redirect-with-key-value', function () { return redirect('dashboard') ->with('error', true); }); // 數組 Route::get('redirect-with-array', function () { return redirect('dashboard') ->with(['error' => true, 'message' => 'Whoops!']); });
你還可使用 withInput()
來重定向用戶的表單輸入; 在驗證錯誤的狀況下,這是最多見的狀況,你但願將用戶發回到剛剛來自的表單。
重定向與表單輸入
Route::get('form', function () { return view('form'); }); Route::post('form', function () { return redirect('form') ->withInput() ->with(['error' => true, 'message' => 'Whoops!']); });
獲取使用 withInput()
傳遞的閃存輸入的最簡單方法是使用 old()
助手,該助手可用於獲取全部舊輸入(old()
)或僅用於特定鍵的值( old('username')
,第二個參數做爲默認值,若是沒有舊值)。 你一般會在視圖中看到這個,它容許在這個表單的 create 和 edit 視圖中使用這個HTML:
<input name="username" value="<?=old('username', 'Default username instructions here');?>">
說到驗證,還有一個有用的方法來將錯誤與重定向響應一塊兒傳遞:withErrors()
。 你能夠傳遞任何錯誤的 "提供者",這多是一個錯誤字符串,一個錯誤數組,或者最多見的是一個 Illuminate
Validator的實例,來看例子。
Route::post('form', function () { $validator = Validator::make($request->all()), $this->validationRules); if ($validator->fails()) { return redirect('form') ->withErrors($validator) ->withInput(); } });
withErrors()
會自動共享一個 $errors
變量,並將其重定向到的頁面的視圖分享,讓你根據本身的喜愛進行處理。
除了返回視圖和重定向以外,退出路由的最多見方式是停止。 有幾個全局可用的方法 (abort()
,abort_if()
和 abort_unless()
),它們將HTTP狀態代碼,消息和頭數組做爲可選參數。 下面的例子中,abort_if()
和 abort_unless()
取第一個參數,它的真實性獲得評估,並根據結果執行取消操做。
Route::post('something-you-cant-do', function (Illuminate\Http\Request) { abort(403, 'You cannot do that!'); abort_unless($request->has('magicToken'), 403); abort_if($request->user()->isBanned, 403); });
還有其餘一些選項可供咱們返回,所以讓咱們在查看,重定向和停止以後回顧最多見的響應。 就像重定向同樣,你可使用 response()
helper 或 Response
facade 來運行這些方法。
若是你想手動建立HTTP響應,只需將你的數據傳遞給 response()->make()
的第一個參數:例如,return response()->make('Hello,World!')
。 第二個參數是HTTP狀態碼,第三個參數是header。
要手動建立JSON編碼的HTTP響應,能夠將你的支持JSON的內容(數組,集合或任何其餘)傳遞給 json()
方法:例如,return response()->json(User::all())
;。 它就像 make()
,除了 json_encode
你的內容並設置適當的header。
要發送文件供最終用戶下載,請將 SplFileInfo
實例或字符串文件名傳遞給 download()
,並使用文件名的可選第二個參數:例如,return response()->download('file20180405.pdf','myFile.pdf')
。 要在瀏覽器中顯示相同的文件(若是它是PDF或圖像或瀏覽器能夠處理的其餘內容),請使用 response()->file()
,它使用與 download()
相同的參數。
Laravel的路由在 routes/web.php
和 routes/api.php
中定義,你能夠在其中定義每一個路由的預期路徑,哪些段是靜態的,哪些是參數,HTTP動詞能夠訪問路由,以及如何解決。 你還能夠將中間件附加到路由,對它們進行分組,並給它們命名。 從路由閉包或控制器方法返回的內容指示Laravel如何響應用戶。 若是它是一個字符串或視圖,它會呈現給用戶; 若是是其餘類型的數據,則將其轉換爲JSON並呈現給用戶; 若是它是重定向,它會強制重定向。 Laravel提供了一系列工具和便利來簡化常見的路由相關任務和結構。 這些包括資源控制器,路由模型綁定和表單方法欺騙。