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