# 快速入門(六):路由
ThinkPHP框架對URL有一定的規(guī)范,所以如果你希望定制你的URL格式的話,就需要好好了解下內(nèi)置的路由功能了,它能讓你的URL變得更簡潔和有內(nèi)涵。
## 路由定義
路由定義一般包括三個(gè)配置參數(shù):
|參數(shù)|描述|
|------|-----------|
|URL_ROUTER_ON|開啟路由,設(shè)置為true后路由規(guī)則定義生效|
|URL_ROUTE_RULES|路由規(guī)則定義|
|URL_MAP_RULES|靜態(tài)路由(URL映射)定義|
要使用路由功能,前提是你的URL支持PATH_INFO(或者兼容URL模式也可以,采用普通URL模式的情況下不支持路由功能),并且在應(yīng)用(或者模塊)配置文件中開啟路由:
~~~
'URL_ROUTER_ON' => true, //開啟路由
~~~
然后就是配置路由規(guī)則了,使用**URL_ROUTE_RULES**參數(shù)進(jìn)行配置,配置格式是一個(gè)數(shù)組,每個(gè)元素都代表一個(gè)路由規(guī)則,例如:
~~~
'URL_ROUTE_RULES'=>array(
'news/:year/:month/:day' => array('News/archive', 'status=1'),
'news/:id' => 'News/read',
'news/read/:id' => '/news/:1',
),
~~~
系統(tǒng)會按定義的順序依次匹配路由規(guī)則,一旦匹配到的話,就會定位到路由定義中的模塊、控制器和操作方法去執(zhí)行(可以傳入其他的參數(shù)),并且后面的規(guī)則不會繼續(xù)匹配。
路由規(guī)則一般是配置在模塊的配置文件中(`模塊名/Conf/config.php`),這樣的話路由規(guī)則僅針對當(dāng)前模塊,并且URL中匹配的路由不能省略當(dāng)前模塊名(除了默認(rèn)模塊外)。如果需要定義全局的路由規(guī)則,就需要把路由配置定義在公共模塊的配置文件中(`Common/Conf/config.php`),但全局路由定義的路由規(guī)則中必須包含明確的模塊。
路由規(guī)則的定義方式如下:
~~~
'路由表達(dá)式'=>'路由地址和額外參數(shù)'
~~~
## 路由表達(dá)式
路由表達(dá)式包括規(guī)則路由和正則路由的定義表達(dá)式,只能使用字符串。
|表達(dá)式 |示例|
|-------|-------|
|正則表達(dá)式 |/^blog\/(\d+)$/|
|規(guī)則表達(dá)式 |blog/:id|
**正則表達(dá)式**
路由表達(dá)式支持的正則定義必須以“/”開頭,否則就視為規(guī)則表達(dá)式。也就是說如果采用
~~~
'#^blog\/(\d+)$#'
~~~
方式定義的正則表達(dá)式不會被支持,而會被認(rèn)為是規(guī)則表達(dá)式進(jìn)行解析,從而無法正確匹配。
~~~
'/^new\/(\d{4})\/(\d{2})$/' => 'News/achive?year=:1&month=:2',
~~~
對于正則表達(dá)式中的每個(gè)變量(即正則規(guī)則中的子模式)部分,如果需要在后面的路由地址中引用,可以采用:1、:2這樣的方式,序號就是子模式的序號。
更多的關(guān)于如何定義正則表達(dá)式就不在本文的描述范疇了。
**規(guī)則表達(dá)式**
規(guī)則路由比正則路由更方便定義和容易理解,規(guī)則表達(dá)式通常包含靜態(tài)地址和動(dòng)態(tài)地址,或者兩種地址的結(jié)合,例如下面都屬于有效的規(guī)則表達(dá)式:
~~~
'my' =>'Member/myinfo', // 靜態(tài)地址路由 類似于之前版本的簡單路由
'blog/:id' =>'Blog/read', // 靜態(tài)地址和動(dòng)態(tài)地址結(jié)合
'new/:year/:month/:day'=>'News/read', // 靜態(tài)地址和動(dòng)態(tài)地址結(jié)合
':user/:blog_id'=>'Blog/read',// 全動(dòng)態(tài)地址
~~~
規(guī)則表達(dá)式的定義以“/”為參數(shù)分割符(無論你的URL_PATHINFO_DEPR設(shè)置是什么,請確保在定義規(guī)則表達(dá)式的時(shí)候統(tǒng)一使用“/”進(jìn)行URL參數(shù)分割)。
每個(gè)參數(shù)中以“:”開頭的參數(shù)都表示動(dòng)態(tài)參數(shù),并且會自動(dòng)對應(yīng)一個(gè)GET參數(shù),例如`:id`表示該處匹配到的參數(shù)可以使用$_GET['id']方式獲取,`:year :month :day` 則分別對應(yīng)`$_GET['year'] $_GET['month'] $_GET['day']`。
**數(shù)字約束**
支持對變量的類型檢測,但僅僅支持?jǐn)?shù)字類型的約束定義,例如
~~~
'blog/:id\d'=>'Blog/read',
~~~
表示只會匹配數(shù)字參數(shù),如果你需要更加多的變量類型檢測,請使用正則表達(dá)式定義來解決。
**規(guī)則排除**
非數(shù)字變量支持簡單的排除功能,主要是起到避免解析混淆的作用,例如:
~~~
'news/:cate^add-edit-delete'=>'News/category'
~~~
因?yàn)橐?guī)則定義的局限性,恰巧我們的路由規(guī)則里面的news和實(shí)際的news模塊是相同的命名,而:cate并不能自動(dòng)區(qū)分當(dāng)前URL里面的動(dòng)態(tài)參數(shù)是實(shí)際的操作名還是路由變量,所以為了避免混淆,我們需要對路由變量cate進(jìn)行一些排除以幫助我們進(jìn)行更精確的路由匹配,格式^add|edit|delete表示,匹配除了add edit 和delete之外的所有字符串,我們建議更好的方式還是改進(jìn)你的路由規(guī)則,避免路由規(guī)則和模塊同名的情況存在,例如
~~~
'new/:cate'=>'News/category'
~~~
就可以更簡單的定義路由規(guī)則了。
**完全匹配**
規(guī)則匹配檢測的時(shí)候只是對URL從頭開始匹配,只要URL地址包含了定義的路由規(guī)則就會匹配成功,如果希望完全匹配,可以使用$符號,例如:
~~~
'new/:cate$'=> 'News/category',
~~~
`http://serverName/index.php/new/info`
會匹配成功
而
`http://serverName/index.php/new/info/2 `
則不會匹配成功
如果是采用
~~~
'new/:cate'=> 'News/category',
~~~
方式定義的話,則兩種方式的URL訪問都可以匹配成功。
## 路由地址和參數(shù)
路由地址和參數(shù)表示前面的路由表達(dá)式最終需要路由到的地址并且允許隱式傳入U(xiǎn)RL里面沒有的一些參數(shù),這里允許使用字符串或者數(shù)組方式定義,支持下面6種方式定義:
|定義方式 |定義格式|
|-----------|------------|
|方式1:路由到內(nèi)部地址(字符串) |'[分組/模塊/操作]?參數(shù)1=值1&參數(shù)2=值2...'|
|方式2:路由到內(nèi)部地址(數(shù)組)參數(shù)采用字符串方式 |array('[分組/模塊/操作]','參數(shù)1=值1&參數(shù)2=值2...')|
|方式3:路由到內(nèi)部地址(數(shù)組)參數(shù)采用數(shù)組方式| array('[分組/模塊/操作]',array('參數(shù)1'=>'值1','參數(shù)2'=>'值2'...))|
|方式4:路由到外部地址(字符串)301重定向| '外部地址'|
|方式5:路由到外部地址(數(shù)組)可以指定重定向代碼| array('外部地址','重定向代碼')|
|方式6:閉包函數(shù) |function($name){ echo 'Hello,'.$name;}|
如果路由地址以“/”或者“http”開頭則會認(rèn)為是一個(gè)重定向地址或者外部地址,例如:
~~~
'blog/:id'=>'/blog/read/id/:1'
~~~
和
~~~
'blog/:id'=>'blog/read/'
~~~
雖然都是路由到同一個(gè)地址,但是前者采用的是301重定向的方式路由跳轉(zhuǎn),這種方式的好處是URL可以比較隨意(包括可以在URL里面?zhèn)魅敫嗟姆菢?biāo)準(zhǔn)格式的參數(shù)),而后者只是支持模塊和操作地址。
舉個(gè)例子,如果我們希望`avatar/123`重定向到
`/member/avatar/id/123_small`的話,只能使用:
~~~
'avatar/:id'=>'/member/avatar/id/:1_small'
~~~
> 路由地址采用重定向地址的話,如果要引用動(dòng)態(tài)變量,也是采用:1、:2 的方式。
采用重定向到外部地址通常對網(wǎng)站改版后的URL遷移過程非常有用,例如:
~~~
'blog/:id'=>'http://blog.thinkphp.cn/read/:1'
~~~
表示當(dāng)前網(wǎng)站(可能是http://thinkphp.cn)的 `blog/123`地址會直接重定向到 `http://blog.thinkphp.cn/read/123`。
在路由跳轉(zhuǎn)的時(shí)候支持額外傳入?yún)?shù)對(額外參數(shù)指的是不在URL里面的參數(shù),隱式傳入需要的操作中,有時(shí)候能夠起到一定的安全防護(hù)作用,后面我們會提到),支持“`參數(shù)1=值1&參數(shù)2=值2`”或者`array('參數(shù)1'=>'值1','參數(shù)2'=>'值2'...)`這樣的寫法,可以參考不同的定義方式選擇。例如:
~~~
'blog/:id'=>'blog/read/?status=1&app_id=5',
'blog/:id'=>array('blog/read/?status=1&app_id=5'),
'blog/:id'=>array('blog/read/','status=1&app_id=5'),
'blog/:id'=>array('blog/read/',array('status'=>1,'app_id'=>5)),
~~~
上面的路由規(guī)則定義中額外參數(shù)的傳值方式都是等效的。`status`和`app_id`參數(shù)都是URL里面不存在的,屬于隱式傳值,當(dāng)然并不一定需要用到,只是在需要的時(shí)候可以使用。
## 閉包支持
我們可以使用閉包的方式定義一些特殊需求的路由,而不需要執(zhí)行控制器的操作方法了,例如:
~~~
'URL_ROUTE_RULES'=>array(
'test' => function(){ echo 'just test'; },
'hello/:name' => function($name){
echo 'Hello,'.$name;
}
)
~~~
**參數(shù)傳遞**
閉包定義的參數(shù)傳遞在規(guī)則路由和正則路由的兩種情況下有所區(qū)別。
規(guī)則路由的參數(shù)傳遞比較簡單:
~~~
'hello/:name' =>
function($name){
echo 'Hello,'.$name;
}
~~~
規(guī)則路由中定義的動(dòng)態(tài)變量的名稱 就是閉包函數(shù)中的參數(shù)名稱,不分次序。 因此,如果我們訪問的URL地址是: `http://serverName/Home/hello/thinkphp`
則瀏覽器輸出的結(jié)果是: `Hello,thinkphp`
如果多個(gè)參數(shù)可以使用:
~~~
'blog/:year/:month' =>
function($year,$month){
echo 'year='.$year.'&month='.$month;
}
~~~
如果是正則路由的話,閉包函數(shù)中的參數(shù)就以正則中出現(xiàn)的參數(shù)次序來傳遞,例如:
~~~
'/^new\/(\d{4})\/(\d{2})$/' =>
function($year,$month){
echo 'year='.$year.'&month='.$month;
}
~~~
如果我們訪問: `http://serverName/Home/new/2013/03` 瀏覽器輸出結(jié)果是: `year=2013&month=03`
## 靜態(tài)路由
靜態(tài)路由其實(shí)屬于規(guī)則路由的靜態(tài)簡化版(又稱為URL映射),路由定義中不包含動(dòng)態(tài)參數(shù),靜態(tài)路由不需要遍歷路由規(guī)則而是直接定位,因此效率較高,但作用也有限。
如果我們定義了下面的靜態(tài)路由
~~~
'URL_ROUTER_ON' => true,
'URL_MAP_RULES'=>array(
'new/top' => 'news/index?type=top'
)
~~~
> 注意:為了不影響動(dòng)態(tài)路由的遍歷效率,靜態(tài)路由采用URL_MAP_RULES定義和動(dòng)態(tài)路由區(qū)分開來
定義之后,如果我們訪問: `http://serverName/Home/new/top`
其實(shí)是訪問: `http://serverName/Home/news/index/type/top`
靜態(tài)路由是完整匹配,所以如果訪問: `http://serverName/Home/new/top/var/test`
盡管前面也有new/top,但并不會被匹配到news/index/type/top。
靜態(tài)路由定義不受URL后綴影響,例如: `http://serverName/Home/new/top.html` 也可以正常訪問。
> 靜態(tài)路由的路由地址 只支持字符串,格式:[控制器/操作?]參數(shù)1=值1&參數(shù)2=值2
## 總結(jié)
通過本篇的學(xué)習(xí),我們大概掌握了如何定義路由,后面我們將會學(xué)習(xí)如何定義視圖和模板賦值。
- 快速入門 1:基礎(chǔ)
- 快速入門 2:CURD
- 快速入門 3:查詢語言
- 快速入門 4:連貫操作
- 快速入門 5:變量
- 快速入門 6:路由
- 快速入門 7:視圖
- 快速入門 8:變量輸出
- 快速入門 9:循環(huán)和控制輸出
- 快速入門 10:公共模板和模板布局
- 快速入門 11:Action參數(shù)綁定
- 快速入門 12:空操作和空控制器
- 快速入門 13:初始化、前置和后置操作
- 快速入門 14:頁面跳轉(zhuǎn)和重定向
- 快速入門 15:頁面請求和AJAX
- 快速入門 16:偽靜態(tài)
- 快速入門 17:操作綁定到類
- 快速入門 18:多層控制器
- 快速入門 19:自動(dòng)驗(yàn)證
- 快速入門 20:自動(dòng)完成
