[TOC]
#### 路由地址
前面我們已經掌握了路由規則和路由變量的使用,路由規則的作用主要是用于匹配檢查,在匹配到正確的路由規則后,并且通過了所有的條件檢查,路由地址就粉墨登場了,不要以為路由地址很簡單,其實不然哦,包含了下面幾種類型的定義方式。
路由地址的作用就是告訴系統該路由規則應該解析到哪去,是到某個控制器方法還是其它的類方法(或者閉包函數)。而模塊/控制器/操作這種路由地址其本質上也是對應到一個類的動態方法,另外也可以路由到一個類的靜態方法,函數的方式是通過閉包來完成(目前路由不支持路由到一個函數名)。
要知道路由地址最終是怎么運作的,其實看think\App類的run方法里面的這段代碼就明白了:
~~~
switch ($dispatch['type']) {
case 'redirect':
// 執行重定向跳轉
$data = Response::create($dispatch['url'], 'redirect')->code($dispatch['status']);
break;
case 'module':
// 模塊/控制器/操作
$data = self::module($dispatch['module'], $config, isset($dispatch['convert']) ? $dispatch['convert'] : null);
break;
case 'controller':
// 執行控制器操作
$data = Loader::action($dispatch['controller']);
break;
case 'method':
// 執行回調方法
$data = self::invokeMethod($dispatch['method']);
break;
case 'function':
// 執行閉包
$data = self::invokeFunction($dispatch['function']);
break;
case 'response':
$data = $dispatch['response'];
break;
default:
throw new \InvalidArgumentException('dispatch type not support');
}
~~~
這段代碼清晰的指出了不同類型的調度是如何執行的,路由類think\Route最終解析后返回的就是一個類似于
~~~
['type' => 'module', 'module' => ['index', 'Index', 'hello']]
~~~
的格式數組,然后App類根據調度類型type來判斷實際需要執行什么,從代碼可以看出,路由類里面實際上有可能返回6種不同的調度類型(除去非法的調度異常之外),而Response對象其實不支持在路由地址中定義,所以實際上就是5種路由地址的定義類型,下面的主要內容就是來講解如何定義這5種不同類型的路由地址。
不同的路由類型其實代表了不同的應用架構模式,大多數情況下的應用普遍采用的是基于模塊/控制器/操作的架構開發模式,也是ThinkPHP5.0使用的默認模式,之所以說是默認模式,是因為關閉路由后的`URL解析`也是按照`模塊/控制器/操作`的方式進行的。
#### 路由到`模塊/控制器/操作`
路由地址的第一種定義方式,也是最常用的方式就是路由到 模塊/控制器/操作 這種,也就是一種模塊化的設計架構,每個URL請求都會最終執行應用的模塊(其實就是一個目錄)下面的控制器(一個控制器類)的操作(控制器類的方法),例如:
~~~
Route::get('hello/:name','index/Index/hello');
~~~
表示路由到index模塊下的Index控制器的hello操作,實際上在匹配到路由規則后,會解析為
~~~
['type' => 'module', 'module' => ['index', 'Index', 'hello']]
~~~
返回給think\App類,然后調用App類的module方法執行相關操作。
同時會進行模塊的初始化操作(包括配置讀取、公共文件載入、行為定義載入、語言包載入等等)。
這種路由類型的路由地址的格式為:
~~~
[模塊/控制器/]操作?參數1=值1&參數2=值2...
~~~
假設一個Blog控制器類定義如下:
~~~
<?php
namespace app\index\controller;
class Blog {
public function read($id){
return 'read:'.$id;
}
}
~~~
定義路由如下:
~~~
// 路由到默認模塊或者綁定模塊
Route::get('blog/:id','blog/read');
// 路由到index模塊
Route::get('blog/:id','index/blog/read');
~~~
路由地址的解析規則從后往前解析,先解析操作,然后解析控制器,最后解析模塊。
雖然路由地址里面的模塊不是必須的,但建議是寫完整的路由地址,避免受其它配置的影響,另外一個也方便路由地址的生成。
路由地址中支持多級控制器(關于多級控制器的概念請參考官方手冊),使用下面的方式進行設置:
~~~
Route::get('blog/:id','index/group.Blog/read');
~~~
表示路由到下面的控制器類
~~~
app\index\controller\group\Blog
~~~
Blog控制器類定義如下:
~~~
<?php
namespace app\index\controller\group;
class Blog
{
public function read($id){
return 'read:'.$id;
}
}
~~~
需要注意的是,如果控制器使用了駝峰命名的話,在路由地址里面也需要使用駝峰方式定義,例如:
~~~
<?php
namespace app\index\controller;
class GroupUser
{
public function read($id){
return 'read:'.$id;
}
}
~~~
用例
~~~
// 錯誤的定義
Route::get('user/:id','index/groupuser/read');
// 正確的定義
Route::get('user/:id','index/GroupUser/read');
~~~
建議在路由地址中定義的控制器名稱和控制器類名保持大小寫一致,方便查看也方便URL地址的統一生成。
還可以支持路由到動態的模塊、控制器或者操作,例如:
~~~
// action變量的值作為操作方法傳入
Route::get(':action/blog/:id','index/Blog/:action');
// 路由到動態的控制器和操作
Route::rule(':controller/:action','index/:controller/:action');
~~~
#### 額外參數
路由地址里面支持額外傳入參數(額外參數指的是不在URL里面的參數,隱式傳入需要的操作中,有時候能夠起到一定的安全防護作用,后面我們會提到)。例如:
~~~
Route::get('blog/:id','index/Blog/read?status=1&app_id=5');
~~~
上面的路由規則定義中額外參數status和app_id參數都是URL里面不存在的,屬于隱式傳值,當然并不一定需要用到,只是在需要的時候可以使用。
> 【5.1須知】
> 可以使用下面的方式
~~~
Route::get('blog/:id','index/Blog/read)
->append(['status'=>1,'app_id'=>5]);
~~~
#### 路由到操作方法
第二種路由類型是直接路由到控制器類的方法執行,這種方式看起來似乎和第一種是一樣的,區別在于直接執行某個控制器類的方法,而不需要去解析 模塊/控制器/操作這些,同時也不會去初始化模塊(也就意味著不會去加載模塊配置和公共文件)。
路由地址的格式為:
@[模塊/控制器/]操作
例如,定義如下路由后:
~~~
Route::get('blog/:id','@index/Blog/read');
~~~
訪問
~~~
http://tp5.com/blog/5
~~~
系統會直接執行
~~~
Loader::action('index/blog/read');
~~~
相當于直接調用`\app\index\controller\blog`類的`read方法`。
通常這種方式下面,由于沒有定義當前模塊名、當前控制器名和當前方法名 ,從而導致視圖的默認模板規則失效,所以這種情況下面,如果使用了視圖模板渲染,則必須傳入明確的參數。
#### 路由到類的方法
第二種路由方式直接執行了控制器類(確切的說是訪問控制器)的方法,如果希望直接執行其它類的方法,就需要使用下面的方式:
路由地址的格式為(動態方法):
> \帶命名空間的完整類名@方法名
或者(靜態方法)
> \帶命名空間完整類名::方法名
這種方式更進一步,可以支持執行任何類的方法,而不僅僅是執行控制器的操作方法,例如:
~~~
Route::get('blog/:id','\app\index\service\Blog@read');
~~~
執行的是 `\app\index\service\Blog類`的`read方法`。
也支持執行某個靜態方法,例如:
~~~
Route::get('blog/:id','\app\index\service\Blog::read');
~~~
#### 路由到重定向地址
重定向的外部地址必須以“`/`”或者`http`開頭的地址。
如果路由地址以“`/`”開頭或者帶有“`://`”的地址會認為是一個重定向地址或者外部地址,例如:
~~~
Route::get('blog/:id','http://blog/read/id/:id');
Route::get('blog/:id','https://blog/read/id/:id');
Route::get('blog/:id','/blog/read/id/:id');
~~~
和
~~~
Route::get('blog/:id','blog/read');
~~~
雖然都是路由到同一個地址,但是前者采用的是301重定向的方式路由跳轉,這種方式的好處是URL可以比較隨意(包括可以在URL里面傳入更多的非標準格式的參數),而后者只是支持模塊和操作地址。舉個例子,如果我們希望`avatar/123`重定向到
`/member/avatar/id/123_small`的話,只能使用:
~~~
Route::get('avatar/:id','/member/avatar/id/:id_small');
~~~
路由地址采用重定向地址的話,如果要引用動態變量,直接使用動態變量即可。
采用重定向到外部地址通常對網站改版后的URL遷移過程非常有用,例如:
~~~
Route::get('blog/:id','http://blog.thinkphp.cn/read/:id');
~~~
表示當前網站(可能是`http://thinkphp.cn `)的 blog/123地址會直接重定向到`http://blog.thinkphp.cn/read/123`
> 【5.1須知】
> 可以使用下面的方式
~~~
Route::redirect('blog/:id','http://blog.thinkphp.cn/read/:id');
~~~
#### 使用閉包定義
我們可以使用閉包的方式定義一些特殊需求的路由,而不需要執行控制器的操作方法了,例如:
~~~
Route::get('hello',function(){
return 'hello,world!';
});
~~~
閉包定義的時候支持參數傳遞,例如:
~~~
Route::get('hello/:name',function($name){
return 'Hello,'.$name;
});
~~~
路由規則中定義的變量名稱就是閉包函數中的參數名稱,不分次序。
因此,如果我們訪問的URL地址是:
~~~
http://tp5.com/hello/thinkphp
~~~
則瀏覽器輸出的結果是:
~~~
Hello,thinkphp
~~~
小結
相信你已經掌握了路由地址的定義規范了,對路由地址的規范總結如下:
|路由類型 |定義格式|
|--|--|
|路由到模塊/控制器/操作| '[模塊/控制器/操作]?額外參數1=值1&額外參數2=值2...'|
|路由到重定向地址| '外部地址'(默認301重定向) 或者 ['外部地址','重定向代碼']|
|路由到控制器的方法 |'@[模塊/控制器/]操作'|
|路由到類的方法 |'\完整的命名空間類::靜態方法' 或者 '\完整的命名空間類@動態方法'|
|路由到閉包函數| 閉包函數定義(支持參數傳入)|
下一篇我會來講解路由分組的用法。