ThinkPHP框架對(duì)URL有一定的規(guī)范,所以如果你希望定制你的URL格式的話,就需要好好了解下內(nèi)置的路由功能了,它能讓你的URL變得更簡(jiǎn)潔和有文化。
啟用路由
要使用路由功能,前提是你的URL支持PATH_INFO,并且在項(xiàng)目配置文件中開(kāi)啟路由:
'URL_ROUTER_ON' => true, //開(kāi)啟路由
然后就是配置路由規(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)會(huì)按定義的順序依次匹配路由規(guī)則,一旦匹配到的話,就會(huì)定位到路由定義中的模塊(支持分組)和操作方法去執(zhí)行,并且后面的規(guī)則不會(huì)繼續(xù)匹配。
路由定義
路由規(guī)則的定義方式如下:
'路由表達(dá)式'=>'路由地址和額外參數(shù)'
路由表達(dá)式
路由表達(dá)式包括規(guī)則路由和正則路由的定義表達(dá)式,只能使用字符串。
表達(dá)式 | 示例 |
---|
正則表達(dá)式 | /^blog/(d+)$/ |
規(guī)則表達(dá)式 | blog/:id |
正則表達(dá)式路由表達(dá)式支持的正則定義必須以“/”開(kāi)頭,否則就視為規(guī)則表達(dá)式。也就是說(shuō)如果采用
'#^blog/(d+)$#'
方式定義的正則表達(dá)式不會(huì)被支持,而會(huì)被認(rèn)為是規(guī)則表達(dá)式進(jìn)行解析,從而無(wú)法正確匹配。
'/^new/(d{4})/(d{2})$/' => 'News/achive?year=:1&month=:2',
對(duì)于正則表達(dá)式中的每個(gè)變量(即正則規(guī)則中的子模式)部分,如果需要在后面的路由地址中引用,可以采用:1、:2這樣的方式,序號(hào)就是子模式的序號(hào)。
更多的關(guān)于如何定義正則表達(dá)式就不在本文的描述范疇了。
規(guī)則表達(dá)式3.0的規(guī)則路由是從2.1的簡(jiǎn)單路由進(jìn)化而來(lái),雖然無(wú)法完美實(shí)現(xiàn)正則路由的功能,但是相比簡(jiǎn)單路由確實(shí)增強(qiáng)了不少,而且比正則路由更方便定義和容易理解。
規(guī)則表達(dá)式通常包含靜態(tài)地址和動(dòng)態(tài)地址,或者兩種地址的結(jié)合,例如下面都屬于有效的規(guī)則表達(dá)式:
'my'=>'Member/myinfo', // 靜態(tài)地址路由 類似于之前版本的簡(jiǎn)單路由
'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ù)分割符(無(wú)論你的URL_PATHINFO_DEPR設(shè)置是什么,請(qǐng)確保在定義規(guī)則表達(dá)式的時(shí)候統(tǒng)一使用“/”進(jìn)行URL參數(shù)分割)。
每個(gè)參數(shù)中以“:”開(kāi)頭的參數(shù)都表示動(dòng)態(tài)參數(shù),并且會(huì)自動(dòng)對(duì)應(yīng)一個(gè)GET參數(shù),例如:id表示該處匹配到的參數(shù)可以使用$_GET['id']方式獲取,:year :month :day 則分別對(duì)應(yīng)$_GET['year'] $_GET['month'] $_GET['day']。
數(shù)字約束支持對(duì)變量的類型檢測(cè),但僅僅支持?jǐn)?shù)字類型的約束定義,例如
'blog/:idd'=>'Blog/read',
表示只會(huì)匹配數(shù)字參數(shù),如果你需要更加多的變量類型檢測(cè),請(qǐng)使用正則表達(dá)式定義來(lái)解決。
規(guī)則排除非數(shù)字變量支持簡(jiǎn)單的排除功能,主要是起到避免解析混淆的作用,例如:
'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í)際的操作名還是路由變量,所以為了避免混淆,我們需要對(duì)路由變量cate進(jìn)行一些排除以幫助我們進(jìn)行更精確的路由匹配,格式^add|edit|delete表示,匹配除了add edit 和delete之外的所有字符串,我們建議更好的方式還是改進(jìn)你的路由規(guī)則,避免路由規(guī)則和模塊同名的情況存在,例如
'new/:cate'=>'News/category'
就可以更簡(jiǎn)單的定義路由規(guī)則了。
完全匹配規(guī)則匹配檢測(cè)的時(shí)候只是對(duì)URL從頭開(kāi)始匹配,只要URL地址包含了定義的路由規(guī)則就會(huì)匹配成功,如果希望完全匹配,可以使用$符號(hào),例如:
'new/:cate$'=> 'News/category',
http://serverName/index.php/new/info
會(huì)匹配成功
而
http://serverName/index.php/new/info/2
則不會(huì)匹配成功
如果是采用
'new/:cate'=> 'News/category',
方式定義的話,則兩種方式的URL訪問(wèn)都可以匹配成功。
路由地址和額外參數(shù)
路由地址和額外參數(shù)表示前面的路由表達(dá)式最終需要路由到的地址并且允許隱式傳入U(xiǎn)RL里面沒(méi)有的一些參數(shù),這里允許使用字符串或者數(shù)組方式定義,支持下面5種方式定義:
定義方式 | 定義格式 |
---|
方式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('外部地址','重定向代碼') |
如果路由地址以“/”或者“http”開(kāi)頭則會(huì)認(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 的方式。
采用重定向到外部地址通常對(duì)網(wǎng)站改版后的URL遷移過(guò)程非常有用,例如:
'blog/:id'=>'http://blog.thinkphp.cn/read/:1'
表示當(dāng)前網(wǎng)站(可能是http://thinkphp.cn)的 blog/123地址會(huì)直接重定向到 http://blog.thinkphp.cn/read/123。
在路由跳轉(zhuǎn)的時(shí)候支持額外傳入?yún)?shù)對(duì)(額外參數(shù)指的是不在URL里面的參數(shù),隱式傳入需要的操作中,有時(shí)候能夠起到一定的安全防護(hù)作用,后面我們會(huì)提到),支持“額外參數(shù)1=值1&額外參數(shù)2=值2”或者array('額外參數(shù)1'=>'值1','額外參數(shù)2'=>'值2'...)這樣的寫(xiě)法,可以參考不同的定義方式選擇。例如:
'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í)候可以使用。
實(shí)例說(shuō)明
通過(guò)上面的講解,我們了解了如何定義路由規(guī)則,下面我們來(lái)舉個(gè)例子加深印象。
假設(shè)我們定義了News控制器如下(代碼實(shí)現(xiàn)僅供參考):
class NewsAction extends Action{
public function read(){
$New = M('New');
if(isset($_GET['id'])) {
// 根據(jù)id查詢結(jié)果
$data = $New->find($_GET['id']);
}elseif(isset($_GET['name'])){
// 根據(jù)name查詢結(jié)果
$data = $New->getByName($_GET['name']);
}
$this->data = $data;
$this->display();
}
public function archive(){
$New = M('New');
$year = $_GET['year'];
$month = $_GET['month'];
$begin_time = strtotime($year . $month . "01");
$end_time = strtotime("+1 month", $begin_time);
$map['create_time'] = array(array('gt',$begin_time),array('lt',$end_time));
$map['status'] = 1;
$list = $New->where($map)->select();
$this->list = $list;
$this->display();
}
}
定義路由規(guī)則如下:
'URL_ROUTE_RULES' => array( //定義路由規(guī)則
'new/:idd' => 'News/read',
'new/:name' => 'News/read',
'new/:yeard/:monthd' => 'News/archive',
),
然后,我們?cè)L問(wèn):
http://serverName/index.php/new/8
會(huì)匹配到第一個(gè)路由規(guī)則,實(shí)際執(zhí)行的效果等效于訪問(wèn):
http://serverName/index.php/News/read/id/8
當(dāng)訪問(wèn):
http://serverName/index.php/new/hello
會(huì)匹配到第二個(gè)路由規(guī)則,實(shí)際執(zhí)行的效果等效于訪問(wèn):
http://serverName/index.php/News/read/name/hello
那么如果訪問(wèn):
http://serverName/index.php/new/2012/03
是否會(huì)匹配第三個(gè)路由規(guī)則呢?我們期望的實(shí)際執(zhí)行的效果能夠等效于訪問(wèn):
http://serverName/index.php/News/archive/year/2012/month/03
事實(shí)上卻沒(méi)有,因?yàn)閔ttp://serverName/index.php/new/2012/這個(gè)URL在進(jìn)行路由匹配過(guò)程中已經(jīng)優(yōu)先匹配到了第一個(gè)路由規(guī)則了,把2012當(dāng)成id的值傳入了,這種情況屬于路由規(guī)則的沖突,解決辦法有兩個(gè):
1、調(diào)整定義順序路由定義改成:
'URL_ROUTE_RULES' => array( //定義路由規(guī)則
'new/:yeard/:monthd' => 'News/archive',
'new/:idd' => 'News/read',
'new/:name' => 'News/read',
),
接下來(lái),當(dāng)我們?cè)俅卧L問(wèn):
http://serverName/index.php/new/2012/03
的時(shí)候,達(dá)到了預(yù)期的訪問(wèn)效果。所以如果存在可能規(guī)則沖突的情況,盡量把規(guī)則復(fù)雜的規(guī)則定義放到前面,確保最復(fù)雜的規(guī)則可以優(yōu)先匹配到。但是如果路由規(guī)則定義多了之后,仍然很容易混淆,所以需要尋找更好的解決辦法。
2、利用完全匹配功能現(xiàn)在我們來(lái)利用路由的完全匹配定義功能,把路由定義改成:
'URL_ROUTE_RULES' => array( //定義路由規(guī)則
'new/:idd$' => 'News/read',
'new/:name$' => 'News/read',
'new/:yeard/:monthd$' => 'News/archive',
),
在規(guī)則最后加上$符號(hào)之后,表示完整匹配當(dāng)前的路由規(guī)則,就可以避免規(guī)則定義的沖突了。對(duì)于規(guī)則路由來(lái)說(shuō),簡(jiǎn)單的理解就是URL里面的參數(shù)數(shù)量或者類型約束要完全一致。
所以,如果我們?cè)L問(wèn)
http://serverName/index.php/new/2012/03/01
的話,是不會(huì)匹配成功任何一條路由的。
3、利用正則路由當(dāng)然,解決問(wèn)題的辦法總是不止一種,對(duì)于復(fù)雜的情況,我們不要忘了使用正則路由規(guī)則定義,在你找不到解決方案的時(shí)候,正則路由總能幫到你。
要實(shí)現(xiàn)上面的同樣路由功能的話,還可以用下面的規(guī)則定義:
'URL_ROUTE_RULES' => array( //定義路由規(guī)則
'/^new/(d+)$/' => 'News/read?id=:1',
'/^new/(w+)$/' => 'News/read?name=:1',
'/^new/(d{4})/(d{2})$/' => 'News/achive?year=:1&month=:2',
),