一般情況下,一個(gè) URL 字符串和它對(duì)應(yīng)的控制器中類(lèi)和方法是一一對(duì)應(yīng)的關(guān)系。 URL 中的每一段通常遵循下面的規(guī)則:
example.com/class/function/id/
但是有時(shí)候,你可能想改變這種映射關(guān)系,調(diào)用一個(gè)不同的類(lèi)或方法,而不是 URL 中對(duì)應(yīng)的那樣。
例如,假設(shè)你希望你的 URL 變成下面這樣:
example.com/product/1/
example.com/product/2/
example.com/product/3/
example.com/product/4/
URL 的第二段通常表示方法的名稱(chēng),但在上面的例子中,第二段是一個(gè)商品 ID , 為了實(shí)現(xiàn)這一點(diǎn),CodeIgniter 允許你重新定義 URL 的處理流程。
路由規(guī)則定義在 app/config/Routes.php 文件中。你將會(huì)在其中看到,該文件創(chuàng)建了一個(gè)RouteCollection類(lèi)的實(shí)例,這一實(shí)例允許你定義自己的路由規(guī)則。 路由中可使用通配符和正則表達(dá)式。
路由通常將URI置于左側(cè),而將控制器和對(duì)應(yīng)的方法以及任何可能存在的,并需要傳遞給控制器的參數(shù)映射在右側(cè)??刂破髋c其方法的列出形式就像你調(diào)用一個(gè)類(lèi)的靜態(tài)方法一樣, 用雙冒號(hào)來(lái)分隔一個(gè)充分命名空間化形式的類(lèi)與其方法,例如 Users::list
。如果這個(gè)方法需要被傳遞參數(shù),這些參數(shù)應(yīng)被以正斜杠分割的形式在方法名后列出,如:
// 調(diào)用 $Users->list()
Users::list
// 調(diào)用 $Users->list(1, 23)
Users::list/1/23
一個(gè)典型的路由規(guī)則看上去就像這樣:
$routes->add('product/(:num)', 'App\Catalog::productLookup');
在一個(gè)路由中,第一個(gè)參數(shù)包含需要被匹配到的URI,而第二個(gè)參數(shù)包含著這個(gè)路由應(yīng)被定位到的目標(biāo)位置。在上述例子中,當(dāng)單詞”product”在URL的第一個(gè)分段中被發(fā)現(xiàn), 同時(shí)在第二個(gè)分段中出現(xiàn)了一個(gè)數(shù)字,那么 App\Catalog
類(lèi)與 productLookup
方法就會(huì)調(diào)用。
通配符是一系列簡(jiǎn)單的正則表達(dá)式類(lèi)型的字符串。在路由處理過(guò)程中,通配符會(huì)被正則表達(dá)式的值所取代,故而這些通配符主要是為了可讀性而設(shè)計(jì)的。
當(dāng)在你的路由處理過(guò)程中,可使用如下通配符:
注解
因?yàn)?{locale} 是一個(gè)系統(tǒng)保留關(guān)鍵詞,用于 localization ,所以不可用于通配符或路由的其他部分。
以下是一些路由示例:
$routes->add('journals', 'App\Blogs');
一個(gè)第一個(gè)分段包含有單詞”journals”的URL將會(huì)被映射于 App\Blogs
類(lèi),這個(gè)類(lèi)的默認(rèn)方法通常將會(huì)是 index()
:
$routes->add('blog/joe', 'Blogs::users/34');
一個(gè)包含有 “blog/joe” 的分段的URL將會(huì)被映射于 \Blogs
類(lèi)和 users
方法,而其ID參數(shù)將會(huì)被置為34:
$routes->add('product/(:any)', 'Catalog::productLookup');
一個(gè)第一個(gè)分段為”product”,并且第二個(gè)分段是任意字符的URl,將會(huì)被映射于 \Catalog
類(lèi)的 productLookup
方法:
$routes->add('product/(:num)', 'Catalog::productLookupByID/$1';
一個(gè)第一個(gè)分段為”product”,并且第二個(gè)分段是數(shù)字的URl,將會(huì)被映射于 \Catalog
類(lèi)的 productLookup
方法,并將這一數(shù)字傳遞為方法的一個(gè)變量參數(shù)。
重要
盡管add()
方法是相當(dāng)方便的,我們還是推薦使用基于HTTP動(dòng)詞的路由結(jié)構(gòu),如下所述,并且這也更為安全。
與此同時(shí),這樣也會(huì)帶來(lái)輕微的性能提升,因?yàn)橹挥衅ヅ洚?dāng)前請(qǐng)求方法的路由會(huì)被保存,從而在搜索路由時(shí)會(huì)減少搜索次數(shù)。
你也可以在路由文件中創(chuàng)建自己的通配符從而實(shí)現(xiàn)用戶體驗(yàn)和可讀性的定制需求。
你可以使用 addPlaceholder
方法來(lái)增加新的通配符。第一個(gè)參數(shù)是一個(gè)被用來(lái)作為通配符的字符串,第二個(gè)是該通配符應(yīng)當(dāng)被替換成的正則表達(dá)式。 這一方法操作需要在你增加路由之前被調(diào)用:
$routes->addPlaceholder('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');
$routes->add('users/(:uuid)', 'Users::show/$1');
如果你更傾向于使用正則表達(dá)式的話,也可以用它來(lái)定義路由規(guī)則。允許任何有效的正則表達(dá)式,例如反向引用。
重要
Note:如果你使用逆向引用,你需要使用美元符號(hào)代替雙斜線語(yǔ)法。一個(gè)典型的使用正則表達(dá)式的路由規(guī)則看起來(lái)像下面這樣:
$routes-&add('products/([a-z]+)/(\d+)', 'Products::show/$1/id_$2');
上例中,一個(gè)類(lèi)似于 products/shirts/123 這樣的 URL 將會(huì)重定向到 Products
控制器的 show
方法。 并且將原來(lái)的第一個(gè)第二個(gè)URI分段作為參數(shù)傳遞給它。通過(guò)正則表達(dá)式,你也可以捕獲一個(gè)帶有斜杠(’/’)的分段,而通常來(lái)說(shuō) 斜杠是用于多個(gè)分段時(shí)間的分隔符。
例如,當(dāng)一個(gè)用戶訪問(wèn)你的 Web 應(yīng)用中的某個(gè)受密碼保護(hù)的頁(yè)面時(shí),如果他沒(méi)有 登陸,會(huì)先跳轉(zhuǎn)到登陸頁(yè)面,你希望在他們?cè)诔晒Φ顷懞笾囟ㄏ蚧貏偛拍莻€(gè)頁(yè)面, 那么這個(gè)例子會(huì)很有用:
$routes->add('login/(.+)', 'Auth::login/$1');
對(duì)于諸位雖然不熟悉正則表達(dá)式而又想了解更多關(guān)于正則表達(dá)式的,W3Cschool的 正則表達(dá)式 教程可能是一個(gè)不錯(cuò)的起點(diǎn)。
重要
注意:你也可以在你的路由規(guī)則中混用通配符和正則表達(dá)式。
你可以使用一個(gè)匿名函數(shù),或者閉包,作為路由的映射目標(biāo)位置。這一函數(shù)將會(huì)在用戶訪問(wèn)指定URI時(shí)執(zhí)行。 以上操作在執(zhí)行小功能,或只是顯示一個(gè)簡(jiǎn)單的視圖時(shí),是相當(dāng)方便的:
$routes->add('feed', function()
{
$rss = new RSSFeeder();
return $rss->feed('general');
});
雖然add()方法非常簡(jiǎn)單易用,但是調(diào)用 map()
方法來(lái)同時(shí)處理多個(gè)路由通常更為方便。 你可以通過(guò)定義一個(gè)路由的數(shù)組,并將其作為 map()
方法的第一個(gè)參數(shù)的批量處理的方式,來(lái)取代每次都要用 add()
方法來(lái)添加所需要路由:
$routes = [];
$routes['product/(:num)'] = 'Catalog::productLookupById';
$routes['product/(:alphanum)'] = 'Catalog::productLookupByName';
$collection->map($routes);
任何存在了足夠長(zhǎng)時(shí)間的網(wǎng)站都肯定存在移動(dòng)過(guò)的頁(yè)面。你可以通過(guò) addRedirect()
方法來(lái)重定向需要跳轉(zhuǎn)到其他路由的路由規(guī)則。 第一個(gè)參數(shù)是原有的路由的URI規(guī)則,第二個(gè)參數(shù)是新的URI,或者是一個(gè)命名路由的名稱(chēng)。第三個(gè)參數(shù)是隨著重定向一起發(fā)送的狀態(tài)碼, 默認(rèn)值 302
,這也是通常情況下用的比較多的,意味著暫時(shí)的重定向:
$routes->add('users/profile', 'Users::profile', ['as' => 'profile']);
// 重定向至命名路由
$routes->addRedirect('users/about', 'profile');
// 重定向至URI
$routes->addRedirect('users/about', 'users/profile');
當(dāng)頁(yè)面加載時(shí),若匹配到重定向路由,則用戶將會(huì)在加載原有控制器之前被重定向到新頁(yè)面。
你可以使用 group()
將你的路由分組并設(shè)定一個(gè)通用的名字。分組名將作為URI的一個(gè)分段,用于組內(nèi)所有定義的路由之前。 這一方式可以幫助你在定義一大組有相同前綴的路由時(shí),減少額外的打字輸入,例如設(shè)置一個(gè)管理分組時(shí):
$routes->group('admin', function($routes)
{
$routes->add('users', 'Admin\Users::index');
$routes->add('blog', 'Admin\Blog::index');
});
如上,’users’和’blog’這些URI就會(huì)加上”amdin”的前綴,從而處理例如 /admin/users
和 /admin/blog
的URI。 如果你需要的話,同樣也可以嵌套分組以便管理:
$routes->group('admin', function($routes)
{
$routes->group('users', function($routes)
{
$routes->add('list', 'Admin\Users::list');
});
});
這將用于處理例如 admin/users/list
的URI。
如果你需要為一個(gè)分組指定指定選項(xiàng),類(lèi)似 namespace ,請(qǐng)?jiān)诨卣{(diào)前使用:
$routes->group('api', ['namespace' => 'App\API\v1'], function($routes)
{
$routes->resource('users');
});
這將能夠使得如同 /api/users/
一樣resource的路由映射于 App\API\v1\Users
控制器上。 你也可以對(duì)一組路由使用一個(gè)特定的過(guò)濾器。過(guò)濾器總是會(huì)在控制器的調(diào)用前或調(diào)用后運(yùn)行,這一操作在認(rèn)證或api日志時(shí)格外有用:
$routes->group('api', ['filter' => 'api-auth'], function($routes)
{
$routes->resource('users');
});
控制器的值必須與定義在 app/Config/Filters.php
中的一系列別名中的至少一個(gè)所匹配。
你可以設(shè)置一組在特定環(huán)境下運(yùn)行的路由。這方便了你創(chuàng)建一組只有開(kāi)發(fā)者在本地環(huán)境中可使用,而在測(cè)試和生產(chǎn)環(huán)境不可見(jiàn)的工具。 以上操作可通過(guò) environment()
方法來(lái)實(shí)現(xiàn)。第一個(gè)參數(shù)是環(huán)境名。在這個(gè)閉包中的定義的所有路由,僅在當(dāng)前環(huán)境下可訪問(wèn):
$routes->environment('development', function($routes)
{
$routes->add('builder', 'Tools\Builder::index');
});
反向路由允許你定義一個(gè)鏈接與它需要查找的當(dāng)前路由所需要使用的控制器和方法以及參數(shù)。這可以不需要改變程序代碼而定義路由規(guī)則。通常用于視圖內(nèi)部以創(chuàng)建鏈接地址。
舉例來(lái)說(shuō),如果你需要一個(gè)跳轉(zhuǎn)到圖片相冊(cè)的路由,你可以使用 route_to()
輔助函數(shù)以獲取當(dāng)前應(yīng)該使用的路由。 第一個(gè)參數(shù)是完整的控制器類(lèi)名與方法名以雙英文冒號(hào)(::)區(qū)分,就像你在寫(xiě)一條原生的路由規(guī)則的格式一樣。其他所有需要傳遞給這個(gè)路由的參數(shù)都將在后面被傳遞:
// 該路由定義為:
$routes->add('users/(:id)/gallery(:any)', 'App\Controllers\Galleries::showUserGallery/$1/$2');
// 生成對(duì)應(yīng)連接到用戶ID1:5,圖片ID:12的指定URL
// 生成:/users/15/gallery/12
<a href="<?= route_to('App\Controllers\Galleries::showUserGallery', 15, 12) ?>">查看相冊(cè)</a>
你可以為路由命名,從而提高系統(tǒng)健壯性(魯棒性),這一操作可通過(guò)給一個(gè)路由命名從而在后面調(diào)用來(lái)實(shí)現(xiàn)。 即使路由定義改變了,所有在系統(tǒng)中通過(guò) route_to
創(chuàng)建的的連接將仍舊可用并且不需要進(jìn)行任何變動(dòng)。 命名一個(gè)路由,通過(guò)與路由名一起傳遞 as
選項(xiàng)來(lái)實(shí)現(xiàn):
// 路由定義為:
$routes->add('users/(:id)/gallery(:any)', 'Galleries::showUserGallery/$1/$2', ['as' => 'user_gallery');
// 生成對(duì)應(yīng)連接到用戶ID1:5,圖片ID:12的指定URL
// 生成:/users/15/gallery/12
<a href="<?= route_to('user_gallery', 15, 12) ?>">View Gallery</a>
這同樣使得視圖更具有可讀性。
還可以在你的路由規(guī)則中使用 HTTP 動(dòng)詞(請(qǐng)求方法),當(dāng)你在創(chuàng)建 RESTFUL 應(yīng)用時(shí)特別有用。 你可以使用所有標(biāo)準(zhǔn)的 HTTP 動(dòng)詞(GET、PUT、POST、DELETE等),每個(gè)動(dòng)詞都擁有自己對(duì)應(yīng)的方法供你使用:
$routes->get('products', 'Product::feature');
$routes->post('products', 'Product::feature');
$routes->put('products/(:num)', 'Product::feature');
$routes->delete('products/(:num)', 'Product::feature');
你可以指定一個(gè)路由可以匹配多個(gè)動(dòng)詞,將其傳遞 match()
方法作為一個(gè)數(shù)組:
$routes->match(['get', 'put'], 'products', 'Product::feature');
你可以使用 cli()
方法來(lái)創(chuàng)建命令行專(zhuān)用,瀏覽器不可訪問(wèn)的路由。 這一方法中創(chuàng)建crojobs(定時(shí)任務(wù))或命令行工具時(shí)相當(dāng)有效。 而基于HTTP動(dòng)詞的路由同樣對(duì)于命令行也是不可訪問(wèn)的,除了通過(guò) any()
方法創(chuàng)建的路由之外:
$routes->cli('migrate', 'App\Database::migrate');
所有用于創(chuàng)建路由的方法(例如add, get, post, resource 等)都可以調(diào)用一個(gè)選項(xiàng)數(shù)組來(lái)修改已生成的路由或限制它們的規(guī)則。而這一數(shù)組 $options
就是這些方法的最后一個(gè)參數(shù):
$routes->add('from', 'to', $options);
$routes->get('from', 'to', $options);
$routes->post('from', 'to', $options);
$routes->put('from', 'to', $options);
$routes->head('from', 'to', $options);
$routes->options('from', 'to', $options);
$routes->delete('from', 'to', $options);
$routes->patch('from', 'to', $options);
$routes->match(['get', 'put'], 'from', 'to', $options);
$routes->resource('photos', $options);
$routes->map($array, $options);
$routes->group('name', $options, function());
你可以通過(guò)指定一個(gè)過(guò)濾器在控制器調(diào)用前或調(diào)用后運(yùn)行的方式來(lái)改變指定路由的行為,這一操作通常在鑒權(quán)或API記錄日志時(shí)非常有用:
$routes->add('admin',' AdminController::index', ['filter' => 'admin-auth']);
過(guò)濾器的值必須至少匹配 app/Config/Filters.php
中的一個(gè)別名。 你也可以指定過(guò)濾器的 before()
和 after()
方法的參數(shù):
$routes->add('users/delete/(:segment)', 'AdminController::index', ['filter' => 'admin-auth:dual,noreturn']);
瀏覽 Controller filters 來(lái)獲取更多有關(guān)設(shè)置篩選過(guò)濾器的信息。
盡管默認(rèn)的命名空間會(huì)在生成的控制器前自動(dòng)附加(如下),你也可以通過(guò) namespace
選項(xiàng)來(lái)指定一個(gè)別的命名空間在選項(xiàng)數(shù)組中。 選項(xiàng)值應(yīng)該與你想指定的命名空間一致:
// 路由指定至 \Admin\Users::index()
$routes->add('admin/users', 'Users::index', ['namespace' => 'Admin']);
新的命名空間僅應(yīng)用于創(chuàng)建一個(gè)單獨(dú)路由的方法調(diào)用中,例如get, post等。對(duì)于創(chuàng)建多個(gè)路由的方法,新的命名空間將會(huì)被附在所有被這個(gè)方法鎖生成的路由之前,例如在 group()
中,所有的路由都是在閉包中生成的。
你可以通過(guò)給選項(xiàng)數(shù)組的”hostname”選項(xiàng)傳一個(gè)域名作為值的形式來(lái)限制一組路由只在你的應(yīng)用的特定域名或子域名下生效:
$collection->get('from', 'to', ['hostname' => 'accounts.example.com']);
這個(gè)例子僅允許當(dāng)前訪問(wèn)的路由在域名為”accounts.example.com”時(shí)生效,而在其主域名”example.com”下無(wú)法生效。
當(dāng) subdomain
選項(xiàng)開(kāi)啟時(shí),系統(tǒng)將會(huì)限制路由僅在此子域名生效。只有在訪問(wèn)該子域名時(shí)系統(tǒng)才會(huì)匹配這組路由規(guī)則:
// 限制子域名為media.example.com
$routes->add('from', 'to', ['subdomain' => 'media']);
你可以通過(guò)設(shè)置該選項(xiàng)值為星號(hào)(*)的方式來(lái)對(duì)所有子域名生效。當(dāng)你訪問(wèn)的URL不匹配任何子域名時(shí),這項(xiàng)路由將不會(huì)被匹配到:
// 限制所有子域名訪問(wèn)
$routes->add('from', 'to', ['subdomain' => '*']);
重要
系統(tǒng)不是完美無(wú)缺的,所以在部署生產(chǎn)環(huán)境前需要在特定的子域名下進(jìn)行測(cè)試。大多數(shù)域名都沒(méi)有問(wèn)題,但在一些邊緣情況下,特別是某些域名本身中就含有點(diǎn)號(hào)(.),而這個(gè)點(diǎn)號(hào)又不是拿來(lái)區(qū)分前綴或者后綴時(shí),就可能會(huì)出錯(cuò)。
你可以向后推移在路由中匹配到的參數(shù)的位置,通過(guò)在 offset
選項(xiàng)中傳遞任何數(shù)字值,該值指名了推移匹配的URI分段的數(shù)量。
這將會(huì)為開(kāi)發(fā)API帶來(lái)好處,當(dāng)URI第一個(gè)分段是版本號(hào)時(shí),同樣可以用于第一個(gè)參數(shù)是一個(gè)語(yǔ)言標(biāo)識(shí)(例如en,fr等,譯者注):
$routes->get('users/(:num)', 'users/show/$1', ['offset' => 1]);
// 創(chuàng)建:
$routes['users/(:num)'] = 'users/show/$2';
(譯者注:實(shí)質(zhì)就是將匹配的位置向后推移,由于第一個(gè)分段的位置可能會(huì)被其他參數(shù)占用,所以通配符的位置需要后移, 例如/en/users/(:num),這里/en/是第一個(gè)分段,不需要作為路由使用,所以(:num)實(shí)際上通過(guò)offset后移到了$2的位置。)
路由集合類(lèi)提供了多個(gè)可影響到所有路由的選項(xiàng)配置,并可被修改以符合程序要求,這些選項(xiàng)可在 /app/Config/Routes/php
文件的頂部被更改。
當(dāng)匹配到了一個(gè)需要路由的控制器,路由將會(huì)為該控制器增加一個(gè)默認(rèn)的命名空間。默認(rèn)設(shè)置下,這個(gè)命名空間的值為空,從而每個(gè)每個(gè)路由都需要完全對(duì)應(yīng)到的帶有命名空間的控制器類(lèi)名:
$routes->setDefaultNamespace('');
// 控制器為 \Users
$routes->add('users', 'Users::index');
// 控制器為 \Admin\Users
$routes->add('users', 'Admin\Users::index');
如果你的控制器不是嚴(yán)格遵從命名空間的話,就沒(méi)有更改的必要。如果你為控制器指定了命名空間,就可以通過(guò)更改默認(rèn)命名空間的值來(lái)減少打字輸入:
$routes->setDefaultNamespace('App');
// 控制器為 \App\Users
$routes->add('users', 'Users::index');
// 控制器為 \App\Admin\Users
$routes->add('users', 'Admin\Users::index');
當(dāng)用戶直接訪問(wèn)你的站點(diǎn)的根路徑時(shí)(例如example.com),所調(diào)用的控制器將會(huì)由 setDefaultController()
方法所設(shè)置的參數(shù)決定,除非有一個(gè)路由是顯式聲明過(guò)(默認(rèn)控制器)。 這一方法的默認(rèn)值是 Home
,對(duì)應(yīng)的控制器是 /app/Controllers/Home.php
// example.com 對(duì)應(yīng)的路由是app/Controllers/Welcome.php
$routes->setDefaultController('Welcome');
默認(rèn)控制器同樣也在找不到對(duì)應(yīng)的路由規(guī)則,URI對(duì)應(yīng)到控制器的對(duì)應(yīng)目錄下的情況下被用到。 例如有個(gè)用戶訪問(wèn)了 example.com/admin
,如果有個(gè)控制器被命名為 /app/Controllers/admin/Home.php
,那么就被調(diào)用到。
與默認(rèn)控制器的設(shè)置類(lèi)似,用于設(shè)置設(shè)置默認(rèn)方法。其應(yīng)用場(chǎng)景是,找到了URI對(duì)應(yīng)的控制器,但是URI分段對(duì)應(yīng)不上控制器的方法時(shí)。默認(rèn)值是 index
$routes->setDefaultMethod('listAll');
在這個(gè)例子中,當(dāng)用戶訪問(wèn)example.com/products時(shí),Products控制器存在,從而執(zhí)行 Products::listAll()
方法。
從它的布爾值就能看出來(lái)這其實(shí)并不是一個(gè)路由,這個(gè)選項(xiàng)可以自動(dòng)的將 URL 中的控制器和方法中的連字符(’-‘)轉(zhuǎn)換為下劃線(’_’),當(dāng)你需要這樣時(shí), 它可以讓你少寫(xiě)很多路由規(guī)則。由于連字符不是一個(gè)有效的類(lèi)名或方法名, 如果你不使用它的話,將會(huì)引起一個(gè)嚴(yán)重錯(cuò)誤:
$routes->setTranslateURIDashes(true);
當(dāng)指定的URI映射不到定義的路由時(shí),系統(tǒng)將會(huì)將URI映射到如上所述的控制器和方法。 你可以通過(guò)設(shè)置 setAutoRoute()
選項(xiàng)為false的方式來(lái)關(guān)閉這一自動(dòng)映射,并限制系統(tǒng)僅使用你定義的路由:
$routes->setAutoRoute(false);
如果當(dāng)前URI匹配不到對(duì)應(yīng)的頁(yè)面,系統(tǒng)將輸出一個(gè)通用的404視圖。你可以通過(guò)使用 set404Override()
方法,定義一個(gè)操作來(lái)改變以上行為。 這一方法的參數(shù)可以是一個(gè)合法的類(lèi)/方法的組合,就如同你在任何路由或者閉包中定義的一樣:
// 將執(zhí)行App\Errors類(lèi)的show404方法
$routes->set404Override('App\Errors::show404');
// 將會(huì)輸出一個(gè)自定義的視圖
$routes->set404Override(function()
{
echo view('my_errors/not_found.html');
});
更多建議: