URI 路由?

一般情況下,一個 URL 字符串和它對應的控制器中類和方法是一一對應的關(guān)系。 URL 中的每一段通常遵循下面的規(guī)則:

example.com/class/function/id/

但是有時候,你可能想改變這種映射關(guān)系,調(diào)用一個不同的類或方法,而不是 URL 中對應的那樣。

例如,假設你希望你的 URL 變成下面這樣:

example.com/product/1/
example.com/product/2/
example.com/product/3/
example.com/product/4/

URL 的第二段通常表示方法的名稱,但在上面的例子中,第二段是一個商品 ID , 為了實現(xiàn)這一點,CodeIgniter 允許你重新定義 URL 的處理流程。

設置你自己的路由規(guī)則?

路由規(guī)則定義在 app/config/Routes.php 文件中。你將會在其中看到,該文件創(chuàng)建了一個RouteCollection類的實例,這一實例允許你定義自己的路由規(guī)則。 路由中可使用通配符和正則表達式。

路由通常將URI置于左側(cè),而將控制器和對應的方法以及任何可能存在的,并需要傳遞給控制器的參數(shù)映射在右側(cè)。控制器與其方法的列出形式就像你調(diào)用一個類的靜態(tài)方法一樣, 用雙冒號來分隔一個充分命名空間化形式的類與其方法,例如 Users::list。如果這個方法需要被傳遞參數(shù),這些參數(shù)應被以正斜杠分割的形式在方法名后列出,如:

// 調(diào)用 $Users->list()
Users::list
// 調(diào)用 $Users->list(1, 23)
Users::list/1/23

通配符?

一個典型的路由規(guī)則看上去就像這樣:

$routes->add('product/(:num)', 'App\Catalog::productLookup');

在一個路由中,第一個參數(shù)包含需要被匹配到的URI,而第二個參數(shù)包含著這個路由應被定位到的目標位置。在上述例子中,當單詞”product”在URL的第一個分段中被發(fā)現(xiàn), 同時在第二個分段中出現(xiàn)了一個數(shù)字,那么 App\Catalog 類與 productLookup 方法就會調(diào)用。

通配符是一系列簡單的正則表達式類型的字符串。在路由處理過程中,通配符會被正則表達式的值所取代,故而這些通配符主要是為了可讀性而設計的。

當在你的路由處理過程中,可使用如下通配符:

  • (:any) 將會從當前位置開始到URI結(jié)束,匹配任何字符。這一通配符可能會包括多個URI分段。
  • (:segment) 將會匹配除了斜杠(/)以外的任何字符,從而將匹配結(jié)果限制在一個單獨的分段中。
  • (:num) 將會匹配任何整數(shù)。
  • (:alpha) 將會匹配任何英文字母字符。
  • (:alphanum) 將會匹配任何英文字母或整數(shù),或者是這兩者的組合。
  • (:hash):segment 相同,但可用于方便地查看那個路由正在使用哈希id(參照 Model )。

注解

因為 {locale} 是一個系統(tǒng)保留關(guān)鍵詞,用于 localization ,所以不可用于通配符或路由的其他部分。

示例?

以下是一些路由示例:

$routes->add('journals', 'App\Blogs');

一個第一個分段包含有單詞”journals”的URL將會被映射于 App\Blogs 類,這個類的默認方法通常將會是 index():

$routes->add('blog/joe', 'Blogs::users/34');

一個包含有 “blog/joe” 的分段的URL將會被映射于 \Blogs 類和 users 方法,而其ID參數(shù)將會被置為34:

$routes->add('product/(:any)', 'Catalog::productLookup');

一個第一個分段為”product”,并且第二個分段是任意字符的URl,將會被映射于 \Catalog 類的 productLookup 方法:

$routes->add('product/(:num)', 'Catalog::productLookupByID/$1';

一個第一個分段為”product”,并且第二個分段是數(shù)字的URl,將會被映射于 \Catalog 類的 productLookup 方法,并將這一數(shù)字傳遞為方法的一個變量參數(shù)。

重要

盡管 add() 方法是相當方便的,我們還是推薦使用基于HTTP動詞的路由結(jié)構(gòu),如下所述,并且這也更為安全。與此同時,這樣也會帶來輕微的性能提升,因為只有匹配當前請求方法的路由會被保存,從而在搜索路由時會減少搜索次數(shù)。

自定義通配符?

你也可以在路由文件中創(chuàng)建自己的通配符從而實現(xiàn)用戶體驗和可讀性的定制需求。

你可以使用 addPlaceholder 方法來增加新的通配符。第一個參數(shù)是一個被用來作為通配符的字符串,第二個是該通配符應當被替換成的正則表達式。 這一方法操作需要在你增加路由之前被調(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');

正則表達式?

如果你更傾向于使用正則表達式的話,也可以用它來定義路由規(guī)則。允許任何有效的正則表達式,例如反向引用。

重要

Note:如果你使用逆向引用,你需要使用美元符號代替雙斜線語法。一個典型的使用正則表達式的路由規(guī)則看起來像下面這樣:

$routes->add('products/([a-z]+)/(\d+)', 'Products::show/$1/id_$2');

上例中,一個類似于 products/shirts/123 這樣的 URL 將會重定向到 Products 控制器的 show 方法。 并且將原來的第一個第二個URI分段作為參數(shù)傳遞給它。通過正則表達式,你也可以捕獲一個帶有斜杠(’/’)的分段,而通常來說 斜杠是用于多個分段時間的分隔符。

例如,當一個用戶訪問你的 Web 應用中的某個受密碼保護的頁面時,如果他沒有 登陸,會先跳轉(zhuǎn)到登陸頁面,你希望在他們在成功登陸后重定向回剛才那個頁面, 那么這個例子會很有用:

$routes->add('login/(.+)', 'Auth::login/$1');

對于諸位雖然不熟悉正則表達式而又想了解更多關(guān)于正則表達式的,regular-expressions.info 可能是一個不錯的起點。

重要

注意:你也可以在你的路由規(guī)則中混用通配符和正則表達式。

閉包?

你可以使用一個匿名函數(shù),或者閉包,作為路由的映射目標位置。這一函數(shù)將會在用戶訪問指定URI時執(zhí)行。 以上操作在執(zhí)行小功能,或只是顯示一個簡單的視圖時,是相當方便的:

$routes->add('feed', function()
{
    $rss = new RSSFeeder();
    return $rss->feed('general');
});

映射多個路由?

雖然add()方法非常簡單易用,但是調(diào)用 map() 方法來同時處理多個路由通常更為方便。 你可以通過定義一個路由的數(shù)組,并將其作為 map() 方法的第一個參數(shù)的批量處理的方式,來取代每次都要用 add() 方法來添加所需要路由:

$routes = [];
$routes['product/(:num)']      = 'Catalog::productLookupById';
$routes['product/(:alphanum)'] = 'Catalog::productLookupByName';

$collection->map($routes);

重定向路由?

任何存在了足夠長時間的網(wǎng)站都肯定存在移動過的頁面。你可以通過 addRedirect() 方法來重定向需要跳轉(zhuǎn)到其他路由的路由規(guī)則。 第一個參數(shù)是原有的路由的URI規(guī)則,第二個參數(shù)是新的URI,或者是一個命名路由的名稱。第三個參數(shù)是隨著重定向一起發(fā)送的狀態(tài)碼, 默認值 302 ,這也是通常情況下用的比較多的,意味著暫時的重定向:

$routes->add('users/profile', 'Users::profile', ['as' => 'profile']);

// 重定向至命名路由
$routes->addRedirect('users/about', 'profile');
// 重定向至URI
$routes->addRedirect('users/about', 'users/profile');

當頁面加載時,若匹配到重定向路由,則用戶將會在加載原有控制器之前被重定向到新頁面。

分組路由?

你可以使用 group() 將你的路由分組并設定一個通用的名字。分組名將作為URI的一個分段,用于組內(nèi)所有定義的路由之前。 這一方式可以幫助你在定義一大組有相同前綴的路由時,減少額外的打字輸入,例如設置一個管理分組時:

$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。

如果你需要為一個分組指定指定選項,類似 namespace ,請在回調(diào)前使用:

$routes->group('api', ['namespace' => 'App\API\v1'], function($routes)
{
        $routes->resource('users');
});

這將能夠使得如同 /api/users/ 一樣resource的路由映射于 App\API\v1\Users 控制器上。 你也可以對一組路由使用一個特定的 過濾器 。過濾器總是會在控制器的調(diào)用前或調(diào)用后運行,這一操作在認證或api日志時格外有用:

$routes->group('api', ['filter' => 'api-auth'], function($routes)
{
    $routes->resource('users');
});

控制器的值必須與定義在 app/Config/Filters.php 中的一系列別名中的至少一個所匹配。

環(huán)境約束?

你可以設置一組在特定環(huán)境下運行的路由。這方便了你創(chuàng)建一組只有開發(fā)者在本地環(huán)境中可使用,而在測試和生產(chǎn)環(huán)境不可見的工具。 以上操作可通過 environment() 方法來實現(xiàn)。第一個參數(shù)是環(huán)境名。在這個閉包中的定義的所有路由,僅在當前環(huán)境下可訪問:

$routes->environment('development', function($routes)
{
        $routes->add('builder', 'Tools\Builder::index');
});

反向路由?

反向路由允許你定義一個鏈接與它需要查找的當前路由所需要使用的控制器和方法以及參數(shù)。這可以不需要改變程序代碼而定義路由規(guī)則。通常用于視圖內(nèi)部以創(chuàng)建鏈接地址。

舉例來說,如果你需要一個跳轉(zhuǎn)到圖片相冊的路由,你可以使用 route_to() 輔助函數(shù)以獲取當前應該使用的路由。 第一個參數(shù)是完整的控制器類名與方法名以雙英文冒號(::)區(qū)分,就像你在寫一條原生的路由規(guī)則的格式一樣。其他所有需要傳遞給這個路由的參數(shù)都將在后面被傳遞:

// 該路由定義為:
$routes->add('users/(:id)/gallery(:any)', 'App\Controllers\Galleries::showUserGallery/$1/$2');

// 生成對應連接到用戶ID1:5,圖片ID:12的指定URL
// 生成:/users/15/gallery/12
<a href="<?= route_to('App\Controllers\Galleries::showUserGallery', 15, 12) ?>">查看相冊</a>

使用命名路由?

你可以為路由命名,從而提高系統(tǒng)健壯性(魯棒性),這一操作可通過給一個路由命名從而在后面調(diào)用來實現(xiàn)。 即使路由定義改變了,所有在系統(tǒng)中通過 route_to 創(chuàng)建的的連接將仍舊可用并且不需要進行任何變動。 命名一個路由,通過與路由名一起傳遞 as 選項來實現(xiàn):

// 路由定義為:
$routes->add('users/(:id)/gallery(:any)', 'Galleries::showUserGallery/$1/$2', ['as' => 'user_gallery');

// 生成對應連接到用戶ID1:5,圖片ID:12的指定URL
    // 生成:/users/15/gallery/12
<a href="<?= route_to('user_gallery', 15, 12) ?>">View Gallery</a>

這同樣使得視圖更具有可讀性。

在路由中使用 HTTP 動詞?

還可以在你的路由規(guī)則中使用 HTTP 動詞(請求方法),當你在創(chuàng)建 RESTFUL 應用時特別有用。 你可以使用所有標準的 HTTP 動詞(GET、PUT、POST、DELETE等),每個動詞都擁有自己對應的方法供你使用:

$routes->get('products', 'Product::feature');
$routes->post('products', 'Product::feature');
$routes->put('products/(:num)', 'Product::feature');
$routes->delete('products/(:num)', 'Product::feature');

你可以指定一個路由可以匹配多個動詞,將其傳遞 match() 方法作為一個數(shù)組:

$routes->match(['get', 'put'], 'products', 'Product::feature');

命令行專用的路由?

你可以使用 cli() 方法來創(chuàng)建命令行專用,瀏覽器不可訪問的路由。 這一方法中創(chuàng)建crojobs(定時任務)或命令行工具時相當有效。 而基于HTTP動詞的路由同樣對于命令行也是不可訪問的,除了通過 any() 方法創(chuàng)建的路由之外:

$routes->cli('migrate', 'App\Database::migrate');

全局選項?

所有用于創(chuàng)建路由的方法(例如add, get, post, resource 等)都可以調(diào)用一個選項數(shù)組來修改已生成的路由或限制它們的規(guī)則。而這一數(shù)組 $options 就是這些方法的最后一個參數(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());

應用過濾器?

你可以通過指定一個過濾器在控制器調(diào)用前或調(diào)用后運行的方式來改變指定路由的行為,這一操作通常在鑒權(quán)或API記錄日志時非常有用:

$routes->add('admin',' AdminController::index', ['filter' => 'admin-auth']);

過濾器的值必須至少匹配 app/Config/Filters.php 中的一個別名。 你也可以指定過濾器的 before()after() 方法的參數(shù):

$routes->add('users/delete/(:segment)', 'AdminController::index', ['filter' => 'admin-auth:dual,noreturn']);

瀏覽 Controller filters 來獲取更多有關(guān)設置篩選過濾器的信息。

指定命名空間?

盡管默認的命名空間會在生成的控制器前自動附加(如下),你也可以通過 namespace 選項來指定一個別的命名空間在選項數(shù)組中。 選項值應該與你想指定的命名空間一致:

// 路由指定至 \Admin\Users::index()
    $routes->add('admin/users', 'Users::index', ['namespace' => 'Admin']);

新的命名空間僅應用于創(chuàng)建一個單獨路由的方法調(diào)用中,例如get, post等。對于創(chuàng)建多個路由的方法,新的命名空間將會被附在所有被這個方法鎖生成的路由之前,例如在 group() 中,所有的路由都是在閉包中生成的。

限制域名?

你可以通過給選項數(shù)組的”hostname”選項傳一個域名作為值的形式來限制一組路由只在你的應用的特定域名或子域名下生效:

$collection->get('from', 'to', ['hostname' => 'accounts.example.com']);

這個例子僅允許當前訪問的路由在域名為”accounts.example.com”時生效,而在其主域名”example.com”下無法生效。

限制子域名?

subdomain 選項開啟時,系統(tǒng)將會限制路由僅在此子域名生效。只有在訪問該子域名時系統(tǒng)才會匹配這組路由規(guī)則:

// 限制子域名為media.example.com
$routes->add('from', 'to', ['subdomain' => 'media']);

你可以通過設置該選項值為星號(*)的方式來對所有子域名生效。當你訪問的URL不匹配任何子域名時,這項路由將不會被匹配到:

// 限制所有子域名訪問
$routes->add('from', 'to', ['subdomain' => '*']);

重要

系統(tǒng)不是完美無缺的,所以在部署生產(chǎn)環(huán)境前需要在特定的子域名下進行測試。大多數(shù)域名都沒有問題,但在一些邊緣情況下,特別是某些域名本身中就含有點號(.),而這個點號又不是拿來區(qū)分前綴或者后綴時,就可能會出錯。

Offsetting the Matched Parameters?

你可以向后推移在路由中匹配到的參數(shù)的位置,通過在 offset 選項中傳遞任何數(shù)字值,該值指名了推移匹配的URI分段的數(shù)量。

這將會為開發(fā)API帶來好處,當URI第一個分段是版本號時,同樣可以用于第一個參數(shù)是一個語言標識(例如en,fr等,譯者注):

$routes->get('users/(:num)', 'users/show/$1', ['offset' => 1]);

// 創(chuàng)建:
$routes['users/(:num)'] = 'users/show/$2';

(譯者注:實質(zhì)就是將匹配的位置向后推移,由于第一個分段的位置可能會被其他參數(shù)占用,所以通配符的位置需要后移, 例如/en/users/(:num),這里/en/是第一個分段,不需要作為路由使用,所以(:num)實際上通過offset后移到了$2的位置。)

路由配置選項?

路由集合類提供了多個可影響到所有路由的選項配置,并可被修改以符合程序要求,這些選項可在 /app/Config/Routes/php` 文件的頂部被更改。

默認命名空間?

當匹配到了一個需要路由的控制器,路由將會為該控制器增加一個默認的命名空間。默認設置下,這個命名空間的值為空,從而每個每個路由都需要完全對應到的帶有命名空間的控制器類名:

$routes->setDefaultNamespace('');

// 控制器為 \Users
$routes->add('users', 'Users::index');

// 控制器為 \Admin\Users
$routes->add('users', 'Admin\Users::index');

如果你的控制器不是嚴格遵從命名空間的話,就沒有更改的必要。如果你為控制器指定了命名空間,就可以通過更改默認命名空間的值來減少打字輸入:

$routes->setDefaultNamespace('App');

// 控制器為 \App\Users
$routes->add('users', 'Users::index');

// 控制器為 \App\Admin\Users
$routes->add('users', 'Admin\Users::index');

默認控制器?

當用戶直接訪問你的站點的根路徑時(例如example.com),所調(diào)用的控制器將會由 setDefaultController() 方法所設置的參數(shù)決定,除非有一個路由是顯式聲明過(默認控制器)。 這一方法的默認值是 Home ,對應的控制器是 /app/Controllers/Home.php

// example.com 對應的路由是app/Controllers/Welcome.php
$routes->setDefaultController('Welcome');

默認控制器同樣也在找不到對應的路由規(guī)則,URI對應到控制器的對應目錄下的情況下被用到。 例如有個用戶訪問了 example.com/admin ,如果有個控制器被命名為 /app/Controllers/admin/Home.php ,那么就被調(diào)用到。

默認方法?

與默認控制器的設置類似,用于設置設置默認方法。其應用場景是,找到了URI對應的控制器,但是URI分段對應不上控制器的方法時。默認值是 index

$routes->setDefaultMethod('listAll');

在這個例子中,當用戶訪問example.com/products時,Products控制器存在,從而執(zhí)行 Products::listAll() 方法。

連字符(-)轉(zhuǎn)換?

從它的布爾值就能看出來這其實并不是一個路由,這個選項可以自動的將 URL 中的控制器和方法中的連字符(’-‘)轉(zhuǎn)換為下劃線(’_’),當你需要這樣時, 它可以讓你少寫很多路由規(guī)則。由于連字符不是一個有效的類名或方法名, 如果你不使用它的話,將會引起一個嚴重錯誤:

$routes->setTranslateURIDashes(true);

僅使用定義路由?

當指定的URI映射不到定義的路由時,系統(tǒng)將會將URI映射到如上所述的控制器和方法。 你可以通過設置 setAutoRoute() 選項為false的方式來關(guān)閉這一自動映射,并限制系統(tǒng)僅使用你定義的路由:

$routes->setAutoRoute(false);

404 重載?

如果當前URI匹配不到對應的頁面,系統(tǒng)將輸出一個通用的404視圖。你可以通過使用 set404Override() 方法,定義一個操作來改變以上行為。 這一方法的參數(shù)可以是一個合法的類/方法的組合,就如同你在任何路由或者閉包中定義的一樣:

// 將執(zhí)行App\Errors類的show404方法
$routes->set404Override('App\Errors::show404');

// 將會輸出一個自定義的視圖
$routes->set404Override(function()
{
    echo view('my_errors/not_found.html');
});