[toc]
概述
由于ThinkPHP5.0默認(rèn)采用的URL規(guī)則是:
http://server/module/controller/action/param/value/...
路由解析的最終結(jié)果通常是把URL地址解析到模塊的某個控制器下的操作方法,在特殊的情況下,也可以跳轉(zhuǎn)到外部地址或者執(zhí)行閉包函數(shù)。
新版的路由功能做了大量的增強(qiáng),包括:
- 支持路由到模塊(模塊/控制器/操作)、控制器(控制器類/操作)、類(任何類庫);
- 閉包路由的增強(qiáng);
- 規(guī)則路由支持全局和局部變量規(guī)則定義(正則);
- 支持路由到任意層次的控制器;
- 子域名路由功能改進(jìn);
- 路由分組并支持分組參數(shù)定義;
- 通過函數(shù)自定義路由檢測規(guī)則;
ThinkPHP5.0的路由比較靈活,系統(tǒng)支持三種方式的URL解析規(guī)則:
一、普通模式
關(guān)閉路由,完全使用默認(rèn)的pathinfo方式URL:
'url_route_on' => false,
路由關(guān)閉后,不會解析任何路由規(guī)則,采用默認(rèn)的PATH_INFO 模式訪問URL:
module/controller/action/param/value/...
但仍然可以通過Action參數(shù)綁定、空控制器和空操作等特性實(shí)現(xiàn)URL地址的簡化。
二、混合模式
開啟路由,并使用路由+默認(rèn)PATH_INFO方式的混合:
'url_route_on' => true,
該方式下面,只需要對需要定義路由規(guī)則的訪問地址定義路由規(guī)則,其它的仍然按照默認(rèn)的PATH_INFO模式訪問URL。
三、強(qiáng)制模式
開啟路由,并設(shè)置必須定義路由才能訪問:
'url_route_on' => true,
'url_route_must'=> true,
這種方式下面必須嚴(yán)格給每一個訪問地址定義路由規(guī)則,否則將拋出異常。
首頁的路由規(guī)則是 /
。
注冊路由規(guī)則
路由功能由think\Route
類實(shí)現(xiàn),包括路由注冊和檢測。
路由注冊可以采用方法動態(tài)單個和批量注冊,也可以直接定義路由定義文件的方式進(jìn)行集中注冊。
動態(tài)注冊
使用Route類的register方法注冊路由規(guī)則(通常可以在應(yīng)用的公共文件中注冊,或者定義配置文件后在公共文件中批量導(dǎo)入的方式注冊),例如注冊如下路由規(guī)則后:
\think\Route::register('new/:id','index/New/read');
我們訪問:
http://serverName/new/5
ThinkPHP5.0的路由規(guī)則定義是從根目錄開始,而不是基于模塊名的。
其實(shí)是訪問的:
http://serverName/index/new/read/id/5
可以在register方法中指定請求類型,不指定的話默認(rèn)為任何請求類型,例如:
\think\Route::register('new/:id','New/update','POST');
表示定義的路由規(guī)則在POST請求下才有效。系統(tǒng)提供了為不同的請求類型定義路由規(guī)則的簡化方法,例如:
\think\Route::get('new/:id','New/read'); // 定義GET請求路由規(guī)則
\think\Route::post('new/:id','New/update'); // 定義POST請求路由規(guī)則
\think\Route::put('new/:id','New/update'); // 定義PUT請求路由規(guī)則
\think\Route::delete('new/:id','New/delete'); // 定義DELETE請求路由規(guī)則
\think\Route::any('new/:id','New/read'); // 所有請求都支持的路由規(guī)則
如果要定義get和post請求支持的路由規(guī)則,也可以用:
\think\Route::register('new/:id','New/read','GET|POST');
我們也可以批量注冊路由規(guī)則,例如:
\think\Route::register(['new/:id'=>'New/read','blog/:name'=>'Blog/detail']);
\think\Route::get(['new/:id'=>'New/read','blog/:name'=>'Blog/detail']);
\think\Route::post(['new/:id'=>'New/update','blog/:name'=>'Blog/detail']);
注冊多個路由規(guī)則后,系統(tǒng)會依次遍歷注冊過的滿足請求類型的路由規(guī)則,一旦匹配到正確的路由規(guī)則后則開始調(diào)用控制器的操作方法,后續(xù)規(guī)則就不再檢測。
定義路由配置文件
如果不希望這么麻煩注冊路由規(guī)則,可以直接在應(yīng)用目錄下面的route.php
直接定義路由規(guī)則,內(nèi)容示例如下:
return [
'__pattern__' => [
'name' => '\w+',
],
'new/:id' => 'New/read',
'[blog]' => [
':id' => ['Blog/read', ['method' => 'get'], ['id' => '\d+']],
':name' => ['Blog/read', ['method' => 'post']],
],
];
__pattern__
表示定義路由變量的全局規(guī)則。更詳細(xì)的使用我們會在后面描述。
路由規(guī)則定義
路由定義由三個部分組成:路由表達(dá)式、路由地址和參數(shù)、路由響應(yīng)的請求類型。
以register方法為例的話就是:
Think\Route::register('路由表達(dá)式','路由地址和參數(shù)','請求類型(默認(rèn)為GET)','匹配參數(shù)(數(shù)組)','變量規(guī)則');
批量注冊的時候規(guī)則是:
\think\Route::register(['路由表達(dá)式'=>'路由地址和參數(shù)',...],'','請求類型(默認(rèn)為GET)','匹配參數(shù)(數(shù)組)','變量規(guī)則');
或者
\think\Route::get(['路由表達(dá)式'=>'路由地址和參數(shù)',...],'','匹配參數(shù)(數(shù)組)','變量規(guī)則'); // GET響應(yīng)路由
\think\Route::post(['路由表達(dá)式'=>'路由地址和參數(shù)',...],'','匹配參數(shù)(數(shù)組)','變量規(guī)則'); // POST響應(yīng)路由
\think\Route::put(['路由表達(dá)式'=>'路由地址和參數(shù)',...],'','匹配參數(shù)(數(shù)組)','變量規(guī)則'); // PUT響應(yīng)路由
為了便于描述路由表達(dá)式和路由地址的對應(yīng)關(guān)系,下面關(guān)于路由規(guī)則的描述定義均以批量定義的形式來表達(dá)。
路由表達(dá)式
路由表達(dá)式統(tǒng)一使用規(guī)則路由表達(dá)式定義,只能使用字符串。
正則路由定義功能已經(jīng)廢除,改由變量規(guī)則定義完成。
規(guī)則表達(dá)式
規(guī)則表達(dá)式通常包含靜態(tài)地址和動態(tài)地址,或者兩種地址的結(jié)合,例如下面都屬于有效的規(guī)則表達(dá)式:
'my'=>'Member/myinfo', // 靜態(tài)地址路由
'blog/:id'=>'Blog/read', // 靜態(tài)地址和動態(tài)地址結(jié)合
'new/:year/:month/:day'=>'News/read', // 靜態(tài)地址和動態(tài)地址結(jié)合
':user/:blog_id'=>'Blog/read',// 全動態(tài)地址
規(guī)則表達(dá)式的定義以“/”為參數(shù)分割符(無論你的URL_PATHINFO_DEPR設(shè)置是什么,請確保在定義規(guī)則表達(dá)式的時候統(tǒng)一使用“/”進(jìn)行URL參數(shù)分割)。
每個參數(shù)中以“:”開頭的參數(shù)都表示動態(tài)參數(shù),并且會自動對應(yīng)一個GET參數(shù),例如:id表示該處匹配到的參數(shù)可以使用$_GET['id']
方式獲取,:year :month :day
則分別對應(yīng)$_GET['year']
$_GET['month']
$_GET['day']
。
完全匹配
規(guī)則匹配檢測的時候只是對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á)式最終需要路由到的地址以及一些需要的參數(shù),支持下面4種方式定義:
定義方式 | 定義格式 |
---|---|
方式1:路由到模塊/控制器 | '[模塊/控制器/操作]?額外參數(shù)1=值1&額外參數(shù)2=值2...' |
方式2:路由到重定向地址 | '外部地址'(默認(rèn)301重定向) 或者 ['外部地址','重定向代碼'] |
方式3:路由到控制器的方法 | '@控制器/操作' |
方式4:路由到類的方法 | '\完整的命名空間類'(靜態(tài)方法) 或者 ['\完整的命名空間類','方法名'] |
路由到模塊/控制器/操作
這是最常用的一種路由地址定義,路由地址中的 [模塊/控制器/操作] 的解析規(guī)則是先從后面的操作開始解析,然后依次往前解析控制器和模塊,如果沒有則用默認(rèn)控制器及默認(rèn)模塊。
如果關(guān)閉路由功能,那么解析規(guī)則其實(shí)就是采用該方式。
路由地址中支持多級控制器,使用下面的方式進(jìn)行設(shè)置:
'blog/:id'=>'index/group.blog/read'
表示路由到下面的控制器類,
index/controller/group/Blog
支持可以動態(tài)改變路由地址,例如:
'blog/:action'=>'index/blog/:action'
路由到重定向地址
重定向的外部地址必須以“/”或者h(yuǎn)ttp開頭的地址。
如果路由地址以“/”或者“http”開頭則會認(rèn)為是一個重定向地址或者外部地址,例如:
'blog/:id'=>'/blog/read/id/:id'
和
'blog/:id'=>'blog/read'
雖然都是路由到同一個地址,但是前者采用的是301重定向的方式路由跳轉(zhuǎn),這種方式的好處是URL可以比較隨意(包括可以在URL里面?zhèn)魅敫嗟姆菢?biāo)準(zhǔn)格式的參數(shù)),而后者只是支持模塊和操作地址。舉個例子,如果我們希望avatar/123重定向到/member/avatar/id/123_small的話,只能使用:
'avatar/:id'=>'/member/avatar/id/:id_small'
路由地址采用重定向地址的話,如果要引用動態(tài)變量,直接使用動態(tài)變量即可。
采用重定向到外部地址通常對網(wǎng)站改版后的URL遷移過程非常有用,例如:
'blog/:id'=>'http://blog.thinkphp.cn/read/:id'
表示當(dāng)前網(wǎng)站(可能是http://thinkphp.cn )的 blog/123地址會直接重定向到 http://blog.thinkphp.cn/read/123。
路由到控制器的方法
這種方式看起來似乎和第一種是一樣的,本質(zhì)的區(qū)別是直接執(zhí)行某個控制器類的方法,而不需要去解析 模塊/控制器/操作 這些。例如,定義如下路由后:
'blog/:id'=>'@index/blog/read',
系統(tǒng)會直接執(zhí)行
\think\Loader::action('index/blog/read');
相當(dāng)于直接調(diào)用 \app\index\controller\blog類的read方法。
路由到類的方法
這種方式更進(jìn)一步,可以支持執(zhí)行任何類的方法,而不僅僅是執(zhí)行控制器的操作方法,例如:
'blog/:id'=>['\app\index\service\Blog','read'],
執(zhí)行的是 \app\index\service\Blog類的read方法。也支持執(zhí)行某個靜態(tài)方法,例如:
'blog/:id'=>'\app\index\service\Blog::read',
額外參數(shù)
在路由跳轉(zhuǎn)的時候支持額外傳入?yún)?shù)對(額外參數(shù)指的是不在URL里面的參數(shù),隱式傳入需要的操作中,有時候能夠起到一定的安全防護(hù)作用,后面我們會提到)。例如:
'blog/:id'=>'blog/read?status=1&app_id=5',
上面的路由規(guī)則定義中額外參數(shù)的傳值方式都是等效的。status和app_id參數(shù)都是URL里面不存在的,屬于隱式傳值,當(dāng)然并不一定需要用到,只是在需要的時候可以使用。
匹配參數(shù)
匹配參數(shù)用于設(shè)置當(dāng)前的路由規(guī)則詳細(xì)的匹配條件,匹配參數(shù)是優(yōu)先于路由表達(dá)式檢測的,目前支持設(shè)置的匹配參數(shù)包括:
參數(shù)名 | 參數(shù)說明 |
---|---|
method | 請求類型 |
ext | 偽靜態(tài)后綴 |
domain | 域名檢測 |
https | 是否https訪問 |
callback | 自定義檢測,可以支持所有is_callable的定義,包括閉包 |
before_behavior | 前置行為(檢測) |
after_behavior | 后置行為(執(zhí)行) |
例如我們可以設(shè)置當(dāng)前的路由規(guī)則只有當(dāng)偽靜態(tài)后綴為html的時候才會匹配:
\think\Route::register('new/:id','New/read','GET',['ext'=>'html']);
或者
\think\Route::get('new/:id','New/read',['ext'=>'html']);
設(shè)置后,URL訪問地址:
http://serverName/new/6
將無法正確匹配,所以會報(bào)錯。只有訪問:
http://serverName/new/6.html
的時候才能正常訪問。
可以支持設(shè)置多個后綴,例如:
\think\Route::get('new/:id','New/read',['ext'=>'html|shtml']);
如果設(shè)置如下:
\think\Route::get('new/:id','New/read',['https'=>true]);
那么訪問
https://serverName/new/6
才是有效匹配的。如果定義了
\think\Route::get('new/:id','New/read',['callback'=>'checkRoute']);
則調(diào)用自定義的checkRoute方法進(jìn)行檢測是否匹配當(dāng)前路由。
變量規(guī)則
ThinkPHP5.0支持在規(guī)則路由中為變量用正則的方式指定變量規(guī)則,彌補(bǔ)了之前變量規(guī)則太局限的問題,并且支持全局規(guī)則設(shè)置。使用方式如下:
設(shè)置全局變量規(guī)則,全局路由有效:
// 設(shè)置name變量規(guī)則(采用正則定義)
\think\Route::pattern('name','\w+');
// 支持批量添加
\think\Route::pattern(['name'=>'\w+','id'=>'\d+']);
局部變量規(guī)則,僅在當(dāng)前路由有效:
// 定義GET請求路由規(guī)則 并設(shè)置name變量規(guī)則
\Think\Route::get('new/:id','New/read',[],['name'=>'\w+']);
如果一個變量同時定義了全局規(guī)則和局部規(guī)則,局部規(guī)則會覆蓋全局變量的定義。
完整URL規(guī)則
如果要對整個URL進(jìn)行變量規(guī)則檢查,可以進(jìn)行__url__
變量規(guī)則(效果相當(dāng)于之前版本的使用正則路由規(guī)則檢測),例如:
// 定義GET請求路由規(guī)則 并設(shè)置完整URL變量規(guī)則
\Think\Route::get('new/:id','New/read',[],['__url__'=>'new\/\w+']);
URL映射
URL映射其實(shí)屬于URL路由的靜態(tài)簡化版,由于不進(jìn)行路由規(guī)則的遍歷操作而是直接定位,因此效率較高,但也因?yàn)槭穷愃朴陟o態(tài)路由的概念,從而作用有限。注冊URL映射的方法如下:
\think\Route::map('new/top','news/index?type=top');
定義之后,如果我們訪問:
http://serverName/new/top
其實(shí)是訪問:
http://serverName/index/news/index/type/top
和路由匹配不同的是,URL映射匹配是完整匹配,所以如果訪問:
http://serverName/new/top/var/test
盡管前面也有new/top,但并不會被匹配到index/news/index/type/top。
URL映射定義也支持批量方式:
\think\Route::map(['new/top'=>'news/index?type=top','blog/new'=>'Blog/index?type=new']);
因?yàn)閁RL映射中的映射規(guī)則是靜態(tài)定義,所以不能含有動態(tài)變量,而且在匹配的時候必須是完全匹配,所以下面的定義是不支持的或者無法生效的:
\think\Route::map('new/:id','news/read');
URL映射無法支持匹配請求類型。
如果你綁定了模塊那么,URL映射會自動加上模塊名。
閉包定義支持
我們可以使用閉包的方式定義一些特殊需求的路由,而不需要執(zhí)行控制器的操作方法了,例如:
\think\Route::get('test',function(){ echo 'just test';});
\think\Route::get('hello/:name',function($name){ echo 'Hello,'.$name;});
參數(shù)傳遞
閉包定義的時候支持參數(shù)傳遞,例如:
\think\Route::get('hello/:name',function($name){ echo 'Hello,'.$name;});
規(guī)則路由中定義的動態(tài)變量的名稱 就是閉包函數(shù)中的參數(shù)名稱,不分次序。因此,如果我們訪問的URL地址是:
http://serverName/hello/thinkphp
則瀏覽器輸出的結(jié)果是:
Hello,thinkphp
資源路由
5.0支持設(shè)置RESTFul請求的資源路由,方式如下:
\think\Route::resource('blog','index/blog');
或者在路由配置文件中使用__rest__
添加資源路由定義:
return [
// 定義資源路由
'__rest__'=>[
'blog'=>'index/blog',
],
// 定義普通路由
'hello/:id'=>'index/hello',
]
設(shè)置后會自動注冊7個路由規(guī)則如下
請求類型 | 生成路由規(guī)則 | 對應(yīng)操作方法 |
---|---|---|
GET | blog |
index |
GET | blog/create |
create |
POST | blog |
save |
GET | blog/:id |
read |
GET | blog/:id/edit |
edit |
PUT | blog/:id |
update |
DELETE | blog/:id |
delete |
具體指向的控制器由路由地址決定,例如上面的設(shè)置,會對應(yīng)index模塊的blog控制器,你只需要為Blog控制器創(chuàng)建以上對應(yīng)的操作方法就可以支持下面的URL訪問:
http://serverName/blog/
http://serverName/blog/128
http://serverName/blog/28/edit
方法如下:
namespace app\index\controller;
class Blog {
public function index(){
}
public function read($id){
}
public function edit($id){
}
}
可以改變默認(rèn)的id參數(shù)名,例如:
\think\Route::resource('blog','index/blog',['var'=>['blog'=>'blog_id']]);
控制器的方法定義調(diào)整如下:
namespace app\index\controller;
class Blog {
public function index(){
}
public function read($blog_id){
}
public function edit($blog_id){
}
}
也可以在定義資源路由的時候限定執(zhí)行的方法,例如:
// 只允許index read edit update 四個操作
\think\Route::resource('blog','index/blog',['only'=>['index','read','edit','update']]);
// 排除index和delete操作
\think\Route::resource('blog','index/blog',['except'=>['index','delete']]);
如果需要更改某個資源路由的對應(yīng)操作,可以使用下面方法:
\think\Route::rest('create',['GET', '/add','add']);
設(shè)置之后,URL訪問變?yōu)椋?/p>
http://serverName/blog/create
變成
http://serverName/blog/add
創(chuàng)建blog頁面的對應(yīng)的操作方法也變成了add。
支持批量更改,如下:
\think\Route::rest([
'save' => ['POST', '', 'store'],
'update' => ['PUT', '/:id', 'save'],
'delete' => ['DELETE', '/:id', 'destory'],
]);
注意,rest數(shù)據(jù)定義的索引
save/update/delete
是不能更改的。
資源嵌套
支持資源路由的嵌套,例如:
\think\Route::resource('blog.comment','index/comment');
就可以訪問如下地址:
http://serverName/blog/128/comment/32
http://serverName/blog/128/comment/32/edit
生成的路由規(guī)則分別是:
blog/:blog_id/comment/:id
blog/:blog_id/comment/:id/edit
Comment控制器對應(yīng)的操作方法如下:
namespace app\index\controller;
class Comment{
public function edit($id,$blog_id){
}
}
如果需要改變其中的變量名,可以使用:
\think\Route::resource('blog.comment','index/comment',['var'=>['blog'=>'blogId']]);
Comment控制器對應(yīng)的操作方法改變?yōu)椋?/p>
namespace app\index\controller;
class Comment{
public function edit($id,$blogId){
}
}
路由分組
路由分組功能允許把相同前綴的路由定義合并分組,這樣可以提高路由匹配的效率,不必每次都去遍歷完整的路由規(guī)則。
例如,我們有定義如下兩個路由規(guī)則的話
'blog/:id' => ['Blog/read', ['method' => 'get'], ['id' => '\d+']],
'blog/:name' => ['Blog/read', ['method' => 'post']],
可以合并到一個blog分組
'[blog]' => [
':id' => ['Blog/read', ['method' => 'get'], ['id' => '\d+']],
':name' => ['Blog/read', ['method' => 'post']],
],
可以使用Route類的group方法進(jìn)行注冊,如下:
\think\Route::group('blog',[
':id' => ['Blog/read', ['method' => 'get'], ['id' => '\d+']],
':name' => ['Blog/read', ['method' => 'post']],
]);
可以給分組路由定義一些公用的路由設(shè)置參數(shù),例如:
\think\Route::group('blog',[
':id' => ['Blog/read', [], ['id' => '\d+']],
':name' => ['Blog/read', [],
],['method'=>'get','ext'=>'html']);
URL生成
可以統(tǒng)一使用 \think\Url類生成URL地址,例如:
\think\Url::build('hello');
\think\Url::build('index/hello');
// 或者使用幫助函數(shù)
U('hello');
U('index/hello');
如果傳入的url地址存在路由定義,會自動轉(zhuǎn)換為路由定義。