前言
本文主要給大家介紹的是關于laravel路由模塊的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。
備注:本文是基于laravel 5.4版本的路由模塊代碼進行分析書寫;
模塊組成
下圖展示了路由模塊中各個文件的關系,并進行簡要說明;
剖析
服務提供者
看laravel模塊,首先找serviceprovider文件,這是模塊與ioc容器交互的入口,從這個文件,可以看出該模塊提供向系統提供了哪些服務;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public function register() { // 注冊路由管理,提供路由注冊,路由匹配的功能 $this ->registerrouter(); // 注冊 url 生成器實例 $this ->registerurlgenerator(); // 注冊跳轉器 $this ->registerredirector(); // 綁定 psr-7 請求實現到 serverrequestinterface 接口 $this ->registerpsrrequest(); // 綁定 psr-7 response 實現到 responseinterface 接口 $this ->registerpsrresponse(); // 注冊 reponsefactory,提供各式各樣的 response,比如視圖響應、json響應、jsonp響應、文件下載等 $this ->registerresponsefactory(); } |
路由管理
“路由管理”服務有以下元素需要了解:
- route:路由;會記錄 url、http 動作、action (路由要執行的具體對象,可能是 closure,也可以是某個 controller 中的方法),路由參數,路由參數的約束;
- routecollection:路由集,用來存儲所有route對象的“盒子”;
- routegroup:路由組;只有路由注冊過程中會臨時用到;存儲一批路由公共的一些屬性,屬性包括domain、prefix、as、middleware、namespace、where;
- resource:資源路由;資源路由是一套路由的統稱,包含列表(index)、顯示增加(create)、保存增加(store)、顯示詳情(show)、顯示編輯詳情(edit)、更新編輯(update)、刪除詳情(destory);同時可以通過調用only或except方法或參數的形式只生成部分路由;
-
action:路由要執行的對象;有兩種表現形式,一是closure函數,二是類似
['uses' => 'foocontroller@method', 'as' => 'name']
這樣的字符串;對于不同的表現形式,路由在執行時會調用不同的處理;
注冊流程
在項目啟動后,會執行所有serviceprovider的loadroutes方法,也就是調用map方法,一般情況下map方法如下
1
2
3
|
public function map(router $router ){ require __dir__. '/routes.php' ; } |
這時候,項目就會執行很多route::get
、route::post
、route::group
方法;
當遇到route::group方法時,會實例化一個routegroup對象,put進router管理類的路由組棧頭部;而后當執行get、post這類具體的注冊路由方法時,會把當前路由組棧中所有組的屬性合并進新路由中,將新路由存儲在routecollection這個大盒子里;當route::group的closure執行完畢時,會把頭部的routegroup實例pull出去;
當執行route::resource時,router管理類會調用resourceregister類來完成批量注冊路由;
對于 router::get這類注冊方法,illuminate\foudation\helpers提供了簡寫;
-
router::get
簡化成 get, -
router::post
簡化成 post, -
router::put
簡化成 put, -
router::patch
簡化成 patch, -
router::delete
簡化成 delete, -
router::resource
簡化成 resource,
至此,routecollection大盒子就存放了所有要注冊的路由;
request 請求匹配流程
首先,request請求會經過foundation/http/kernel的handle方法,在這個方法中,請求會執行以下語句
1
|
$this ->router->dispatch( $request ) |
這里的$this->router
,就是router管理類;dispatch方法如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public function dispatch(request $request ) { $this ->currentrequest = $request ; return $this ->dispatchtoroute( $request ); } public function dispatchtoroute(request $request ) { // 根據請求的 url 找到匹配的路由 $route = $this ->findroute( $request ); // 將路由綁定到請求上 $request ->setrouteresolver( function () use ( $route ) { return $route ; } // 觸發 routematched 事件 $this ->events->dispatch( new events\routematched( $route , $request )); // 通過 pipeline 流水線執行路由上綁定的中間件及對應的方法 $response = $this ->runroutewithinstack( $route , $request ); // 根據 request 請求設置 response 的響應頭 return $this ->prepareresponse( $request , $response ); } |
1、根據請求找匹配的路由
`routecollection`根據請求的`http`動作縮小要匹配的路由范圍;在篩選出來的這些路由中依次遍歷,找出第一個符合驗證的路由(需要進行較驗的驗證在`route`中的`getvalidators`方法中聲明);
2、將路由綁定到請求上
3、觸發routematched事件
初始化的`laravel`項目沒有對`routematched`路由匹配事件進行任何的監聽器綁定,如有需要,可以自定義監聽器,在模塊的`eventserviceprovider`中注冊該事件監聽;這樣一旦請求匹配上某個路由,就可以執行自定義方法了;
4、通過 pipeline 流水線執行路由上綁定的中間件及對應的方法
在`runroutewithinstack`方法中,系統會判斷是否需要執行中間件,如果`ioc`容器中設置了`middleware.disable`的值為`true`,則需要執行的中間件數組為空;否則會找到所有的中間件,并按照`middlewarepriority`對必要的一些中間件進行排序調整;然后執行`$route->run()`
方法;
5、根據 request 請求設置 response 的響應頭
項目中會用到的一些方法
-
獲取路由集合
app('router')->getroutes()
-
獲取當前的請求
$request = app('router')->getcurrentrequest()
-
獲取當前請求所對應的路由
$route = $request->route() 或 $route = app('router')->getcurrentroute()
-
獲取當前路由需要執行的中間件
$middlewares = app('router')->gatherroutemiddleware($route)
url 生成器
url 生成器是什么?
舉個例子,
1
2
3
4
5
6
|
$url = new urlgenerator( $routes = new routecollection, $request = request::create( 'http://www.foo.com/' ) ); $url ->to( 'foo/bar' ); // 輸出 http://www.foo.com/foo/bar |
像這種基于當前請求,生成指定路徑的url;
這部分功能由兩個文件完成,一個是urlgenerator.php,另一個是routeurlgenerator.php;urlgenerator.php處理根據路徑名生成url,routeurlgenerator.php處理根據路由生成url;
列一些常用的使用:
根據路徑名生成
使用to方法,第一個參數為路徑,第二個參數是數組,implode后會接著路徑名,第三個參數決定用不用https
1
2
3
4
5
6
7
8
9
|
// 路徑名是 foo/bar,當前請求的根路徑為 http://www.foo.com,所以輸出是 http://www.foo.com/foo/bar $url ->to( 'foo/bar' ) // 路徑名是 foo/bar,當前請求的根路徑為 http://www.foo.com,第三個參數決定 scheme 是 https,所以輸出是 https://www.foo.com/foo/bar $url ->to( 'foo/bar' , [], true) // 路徑名是 foo/bar,第二個參數 是補充路徑名,implode 后是 /baz/boom // 第三個參數決定 scheme 是 https,所以輸出是 https://www.foo.com/foo/bar/baz/boom $url ->to( 'foo/bar' , [ 'baz' , 'boom' ], true) // 路徑名是 foo/bar,查詢參數是 ?foo=bar ,補充路徑是 /baz,所以輸出是 https://www.foo.com/foo/bar/baz?foo=bar $url ->to( 'foo/bar?foo=bar' , [ 'baz' ], true) |
根據路由的 as 名生成
使用route方法,第一個參數為指定路由的 as 名,第二個參數是參數數組,第三個參數決定是否顯示根目錄(默認為 true)
1
2
3
4
5
6
7
8
9
10
11
12
|
$route = new route([ 'get' ], 'foo/bar' , [ 'as' => 'foo' ]); $routes ->add( $route ); // 輸出 'http://www.foo.com/foo/bar $url ->route( 'foo' ); // 第三個參數為 false,表示不顯示根目錄,于是輸出 /foo/bar $url ->route( 'foo' , [], false) // 路由中的 url 本身不帶參數,則第二參數中所有關聯數組都將作為查詢參數 // 輸出 /foo/bar?foo=bar $url ->route( 'foo' , [ 'foo' => 'bar' ], false) |
1
2
3
4
5
6
7
8
9
10
|
$route = new route([ 'get' ], 'foo/bar/{baz}/breeze/{boom}' , [ 'as' => 'bar' ]); $routes ->add( $route ); // 路由上的 url 帶參數,根據參數名找值;剩余多余的為查詢參數; // 輸出 http://www.foo.com/foo/bar/otwell/breeze/taylor?fly=wall $url ->route( 'bar' , [ 'boom' => 'taylor' , 'baz' => 'otwell' , 'fly' => 'wall' ]); // 路由上的 url 帶參數,找不到對應的參數值,則按順序作值;剩余多余的為查詢參數; // 輸出 http://www.foo.com/foo/bar/taylor/breeze/otwell?fly=wall $url ->route( 'bar' , [ 'taylor' , 'otwell' , 'fly' => 'wall' ]); |
根據路由的 action 名生成
使用action方法,第一個參數為指定路由的 action 名,第二個參數是參數數組,第三個參數決定是否顯示根目錄(默認為 true)
1
2
3
4
5
|
$route = new route([ 'get' ], 'foo/bam' , [ 'controller' => 'foo@bar' ]); $routes ->add( $route ); // 輸出 http://www.foo.com/foo/bam $url ->action( 'foo@bar' ); |
1
2
3
4
5
|
$route = new route([ 'get' ], 'foo/invoke' , [ 'controller' => 'invokableactionstub' ]); $routes ->add( $route ); // 輸出 http://www.foo.com/foo/invoke $url ->action( 'invokableactionstub' ); |
設置全局默認參數
1
2
3
4
5
6
|
$url ->defaults([ 'locale' => 'en' ]); $route = new route([ 'get' ], 'foo' , [ 'as' => 'defaults' , 'domain' => '{locale}.example.com' , function () {}]); // 路由 url 有參數,但沒有傳參數值,則會找全局默認參數值;輸出 http://en.example.com/foo $url ->route( 'defaults' ); |
設置全局命名空間
這樣調用的時候,不用在 action 上省略這部分命名空間
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// 設置全局命名空間 $url ->setrootcontrollernamespace( 'namespace' ); // 配置添加路由 $route = new route([ 'get' ], 'foo/bar' , [ 'controller' => 'namespace\foo@bar' ]); $routes ->add( $route ); $route = new route([ 'get' ], 'foo/invoke' , [ 'controller' => 'namespace\invokableactionstub' ]); $routes ->add( $route ); // 輸出 http://www.foo.com/foo/bar; action 的值省略 namespace 這個命名空間 $url ->action( 'foo@bar' ); // 輸出 http://www.foo.com/foo/invoke; action 的值省略 namespace 這個命名空間 $url ->action( 'invokableactionstub' ); // 配置添加路由 $route = new route([ 'get' ], 'something/else' , [ 'controller' => 'something\foo@bar' ]); $routes ->add( $route ); // 輸出 http://www.foo.com/something/else; action 的最前面加了 `\`,全局命名空間下調用 $url ->action( '\something\foo@bar' ); |
跳轉器
跳轉器內部提供了以下跳轉;
home
通過調用app('redirect')->home()
會跳轉至根目錄下\;
1
|
public function home( $status = 302) |
back
通過調用app('redirect')->back()
會跳轉至上一次訪問頁面;或者全局幫助函數back()
也可以;
1
|
public function back( $status = 302, $headers = [], $fallback = false) |
第三個參數表示,如果沒有前一次訪問請求,訪問哪個頁面,具體源碼如下:
1
2
3
4
5
6
7
|
if ( $url ) { return $url ; } elseif ( $fallback ) { return $this ->to( $fallback ); } else { return $this ->to( '/' ); } |
refresh
通過調用app('redirect')->refresh()
會刷新當前訪問頁面;
1
|
public function refresh( $status = 302, $headers = []) |
to
通過調用app('redirect')->to('path')
會跳轉至指定路徑頁面;或者全局幫助函數redirect('path')
也可以;
這里的 path 路徑是不包含根目錄的,例如(foo/bar);
1
|
public function to( $path , $status = 302, $headers = [], $secure = null) |
第四個參數表示是否使用https;
away
通過調用app('redirect')->away('path')
會跳轉至指定路徑頁面;
這里的 path 路徑是包含根目錄的,例如(http://xx.com/foo/bar);
1
|
public function away( $path , $status = 302, $headers = []) |
secure
通過調用app('redirect')->secure('path')
會跳轉至指定路徑頁面;這里的path路徑是不包含根目錄的;
1
|
public function secure( $path , $status = 302, $headers = []) |
其本質是調用了to方法
1
|
return $this ->to( $path , $status , $headers , true); |
route
通過調用app('redirect')->route('route_as_name')
,根據路由的as名會跳轉至與路由一致的url路徑頁;
1
|
public function route( $route , $parameters = [], $status = 302, $headers = []) |
action
通過調用app('redirect')->action('route_action')
,根據路由的action名會跳轉至與路由一致的url路徑頁;
1
|
public function action( $action , $parameters = [], $status = 302, $headers = []) |
guest
跳到指定的路徑頁的同時,將當前url存放至session中,鍵名為url.intended;
1
|
public function guest( $path , $status = 302, $headers = [], $secure = null) |
intended
跳轉至session中鍵名為url.intended的值所對應的url;如果不存在,則跳轉至第一個參數所傳的值;
1
|
public function intended( $default = '/' , $status = 302, $headers = [], $secure = null) |
響應工廠(responsefactory)
responsefactory文件提供了兩部分 api,分別是與響應類型相關和與跳轉相關;
響應
response()
會返回responsefactory實例;
視圖響應
1
|
response()->view( 'hello' , $data , 200); |
jsop響應
1
|
response()->json([ 'name' => 'abigail' , 'state' => 'ca' ]); |
jsonp響應
1
|
response()->json([ 'name' => 'abigail' , 'state' => 'ca' ])->withcallback( $request ->input( 'callback' )); |
文件響應
直接在瀏覽器顯示文件,而不是下載,例如圖片或pdf;file方法第一參數為文件路徑,第二參數選填為頭信息數組;
1
|
response()->file( $pathtofile , $headers ); |
文件下載
download方法第一參數為文件路徑,第二參數選填為文件名,第三參數選填為頭信息數組;
1
|
return response()->download( $pathtofile , $name , $headers ); |
跳轉
這里的跳轉方法,其實調用的還是跳轉器中的方法,不過是在暴露更多的接口,方便調用與使用;
方法名 | 調用 | 實際調用的是跳轉器中的哪個方法 |
---|---|---|
redirectto | response()->redirectto(...) | to方法 |
redirecttoroute | response()->redirecttoroute(...) | route方法 |
redirecttoaction | response()->redirecttoaction(...) | action方法 |
redirectguest | response()->redirectguest(...) | guest方法 |
redirecttointended | response()->redirecttointended(...) | intended方法 |
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:https://segmentfault.com/a/1190000010440480