URL 網址管理 ============== 網路應用程式完整的 URL 管理包括兩個方面: 1. When a user request comes in terms of a URL, the application needs to parse it into understandable parameters. 2. The application needs to provide a way of creating URLs so that the created URLs can be understood by the application. 1. 當使用者請求一個 URL,應用程式程式需要解析它變成可以理解的參數。 2. 應用程式程式需求提供一種創造 URL 的方法,以便建立的 URL 應用程式程式可以理解的。 對於Yii應用程式程式,這些透過 [CUrlManager] 輔助完成。 建立 URL ------------- 雖然URL可被寫死在控制器的視圖文件,但往往可以很靈活地動態建立它們: ~~~ [php] $url=$this->createUrl($route,$params); ~~~ `$this` 指的是控制器實體;`$route` 指定請求的 [route](/doc/guide/basics.controller#route) 的要求;`$params` 列出了附加在網址中的 `GET` 參數。 預設情況下,URL 以 `get` 格式使用 [createUrl|CController::createUrl] 建立。例如,提供 `$route='post/read'` 和 `$params=array('id'=>100)`,我們將獲得以下網址: ~~~ /index.php?r=post/read&id=100 ~~~ 參數以一系列 `Name=Value` 通過符號串聯起來出現在請求字串,`r` 參數指的是請求的 [route](/doc/guide/basics.controller#route)。這種 URL 格式使用者友善不是很好,因為它需要一些非字字符。 我們可以使上述網址看起來更簡潔,更不言自明,通過採用所謂的 `path` 格式,省去查詢字串和把 GET 參數加到路徑訊息,作為網址的一部分: ~~~ /index.php/post/read/id/100 ~~~ 要更改 URL 格式,我們應該配置 [urlManager|CWebApplication::urlManager] 應用程式元件,以便 [createUrl|CController::createUrl] 可以自動切換到新格式和應用程式程式可以正確理解新的網址: ~~~ [php] array( ...... 'components'=>array( ...... 'urlManager'=>array( 'urlFormat'=>'path', ), ), ); ~~~ 請注意,我們不需要指定的 [urlManager|CWebApplication::urlManager] 元件的類別,因為它在 [CWebApplication] 事先宣告為[CUrlManager]。 > 提示:此網址通過 [createUrl|CController::createUrl] 方法所產生的是一個相對地址。為了得到一個絕對的 URL ,我們可以用前綴 `Yii::app()->hostInfo`,或調用 [createAbsoluteUrl|CController::createAbsoluteUrl]。 使用者友善的 URL ------------------ 當用 `path` 格式 URL,我們可以指定某些 URL 規則使我們的網址更使用者友善。例如,我們可以產生一個短短的 URL `/post/100`,而不是冗長 `/index.php/post/read/id/100`。網址建立和解析都是通過 [CUrlManager] 指定網址規則。 要指定的URL規則,我們必須設定 [urlManager|CWebApplication::urlManager] 應用程式元件的屬性 [rules|CUrlManager::rules]: ~~~ [php] array( ...... 'components'=>array( ...... 'urlManager'=>array( 'urlFormat'=>'path', 'rules'=>array( 'pattern1'=>'route1', 'pattern2'=>'route2', 'pattern3'=>'route3', ), ), ), ); ~~~ 這些規則被指定成一系列的路由模式配對陣列,每配對對應於一個單一的規則。路由的格式必須是有效的正則表達式,沒有分隔符和修飾語。它是用於匹配網址的路徑訊息部分。還有 [route](/doc/guide/basics.controller#route)應指向一個有效的路由控制器。 除此之外,一個規則可以指定客製化的選項。如下所示: ~~~ [php] 'pattern1'=>array('route1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false) ~~~ 上述,該陣列包含了一些客製化的選項。版本 1.1.0 後,有這些選項可供選擇: - [urlSuffix|CUrlRule::urlSuffix]: URL 規則的後綴。預設是 null,表示使用 [CUrlManager::urlSuffix] 的值。 - [caseSensitive|CUrlRule::caseSensitive]: 是否大小寫敏感。預設是 null,表示使用 [CUrlManager::caseSensitive] 的值。 - [defaultParams|CUrlRule::defaultParams]: 預設的 GET 參數 (name=>value) 規則。當這個規則被用來剖析傳入的需求時,這屬性的值將會被置入 $_GET。 - [matchValue|CUrlRule::matchValue]: 當建立的 URL 時,GET 參數的值是否與規則的子樣式匹配。預設是空值,代表使用 [CUrlManager::matchValue] 的值。如果這個屬性是 false,代表一個規則會被用來建立 URL ,如果他的路由和給定的參數相互對應。如果這個屬性是 true,納給定的參數值必須和子模式的參數相對應。注意一點,將這個屬性設為 true 會降低效能。 ### 使用命名的參數 一個規則可以和一些 GET 參數相關聯。這些 GET 參數會以特殊的符號格式出現在規則的樣式裡,如下所示: ~~~ <ParamName:ParamPattern> ~~~ `ParamName` 表示 GET 參數名字,可選項 `ParamPattern` 表示將用於匹配 GET 參數值的正則表達式。當產生一個 URL 時,這些參數符號將被相應的參數值替換;當解析一個網址時,相應的 GET 參數將通過解析結果來產生。 我們使用一些例子來解釋網址工作規則。我們假設我們的規則包括如下三個: ~~~ [php] array( 'posts'=>'post/list', 'post/'=>'post/read', 'post//'=>'post/read', ) ~~~ - 調用 `$this->createUrl('post/list')` 產生 `/index.php/posts`。第一個規則適用。 - 調用 `$this->createUrl('post/read',array('id'=>100))` 產生 `/index.php/post/100`。第二個規則適用。 - 調用 `$this->createUrl('post/read',array('year'=>2008,'title'=>'a sample post'))` 產生 `/index.php/post/2008/a%20sample%20post`。第三個規則適用。 - 調用 `$this->createUrl('post/read')` 產生 `/index.php/post/read`。請注意,沒有規則適用。 總之,當使用 [createUrl|CController::createUrl] 產生網址,路由和傳遞給該方法的 GET 參數被用來決定哪些網址規則適用。如果關聯規則中的每個參數可以在 GET 參數找到的,將被傳遞給 [createUrl|CController::createUrl],如果路由的規則也匹配路由參數,規則將用來產生網址。 如果 GET 參數傳遞到[createUrl|CController::createUrl]是以上所要求的一項規則,其他參數將出現在查詢字串。例如,如果我們調用 `$this->createUrl('post/read',array('id'=>100,'year'=>2008))` ,我們將獲得 `/index.php/post/100?year=2008`。為了使這些額外參數出現在路徑訊息的一部分,我們應該給規則附加 `/*`。 因此,該規則 `post/<id:\d+>/*`,我們可以取得網址 `/index.php/post/100/year/2008`。 正如我們提到的,URL 規則的其他用途是解析請求網址。當然,這是 URL 產生的一個逆向過程。例如, 當使用者請求 `/index.php/post/100`,上面例子的第二個規則將適用來解析路由 `post/read` 和 GET 參數 `array('id'=>100)`(可通過 `$_GET` 獲得)。 > 註:使用的URL規則將降低應用程式的性能。這是因為當解析請求的 URL,[ CUrlManager ]嘗試使用每個規則來匹配它,直到某個規則可以適用。因此,高流量網站應用程式應盡量減少其使用的 URL 規則。 ### 參數化路由 我們或許會在一個規則的路由的部分參照命名參數。這將允許一個規則被用在許多路由匹配標準。他也會幫助減少一個應用程式需要的規則數量和增進整體的效能。 我們使用如下的範例規則來描述如何使用命名參數來參數化路由: ~~~ [php] array( '<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>' => '<_c>/<_a>', '<_c:(post|comment)>/<id:\d+>' => '<_c>/read', '<_c:(post|comment)>s' => '<_c>/list', ) ~~~ 如上所述,我們使用兩個命名參數在一個規則的路由部分:`_c` 和 `_a`。前者與控制器名稱 `post` 或 `comment` 配對,而後者與動作名稱 `create`、`update` 或 `delete`配對。你可以任意命名參數只要他們不與 URL 裡的 GET 參數產生衝突。 使用上述的規則,URL `/index.php/post/123/create` 會被解析成路由 `post/create` 帶著 GET 參數 `id=123`。和路由 `comment/list` 帶著 GET 參數 `page=2`,我們可以建立一個 URL `/index.php/comments?page=2`。 ### 參數化主機名稱 包含主機名稱到規則裡來解析和建立 URL 也是可以的。可以擷取主機的部分名稱來當作 GET 參數。例如, URL `http://admin.example.com/en/profile` 可以被解析成 GET 參數 `user=admin` 和 `lang=en`。換句話說,帶有主機名稱的規則可以被用來建立參數化的主機名稱。 使用參數化的主機名稱,只需要簡單的宣告 URL 例如: ~~~ [php] array( 'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile', ) ~~~ 上述的範例說明了主機名稱的第一個區段會被當成 `user` 參數,路徑的第一個區段會被當成 `lang` 參數。這個規則 `user/profile` 路由相對應。 注意,當一個 URL 以參數化主機名稱建立時, [CUrlManager::showScriptName] 不會產生作用。 另外,如果一個應用程式在網站根目錄的子目錄下,一個參數化主機名稱規則不應該包含該子目錄。例如,如果一個應用程式是在 `http://www.example.com/sandbox/blog` 下,我們仍應使用上述相同的 URL 規則且不包含子目錄 `sandbox/blog`。 ### 隱藏 `index.php` 還有一點,我們可以進一步清理我們的網址,即在 URL 中藏匿 `index.php` 入口腳本。這就要求我們配置網路伺服器,以及 [urlManager|CWebApplication::urlManager] 應用程式程式元件。 我們首先需要配置網路伺服器,這樣一個 URL 沒有入口腳本仍然可以處理入口腳本。如果是 [Apache HTTP server](http://httpd.apache.org/),可以通過打開網址重寫引擎和指定一些重寫規則。這兩個操作可以在包含入口腳本的目錄下的 `.htaccess` 文件裡實現。下面是一個範例: ~~~ Options +FollowSymLinks IndexIgnore */* RewriteEngine on # if a directory or a file exists, use it directly RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # otherwise forward it to index.php RewriteRule . index.php ~~~ 然後,我們設定 [urlManager|CWebApplication::urlManager] 元件的 [showScriptName|CUrlManager::showScriptName] 屬性為 `false`。 現在,如果我們調用 `$this->createUrl('post/read',array('id'=>100))`,我們將取得網址 `/post/100` 。更重要的是,這個URL可以被我們的網路應用程式程式正確解析。 ### 偉造的 URL 後綴 我們還可以增加一些網址的後綴。例如,我們可以用 `/post/100.html` 來替代 `/post/100` 。這使得它看起來更像一個靜態網頁 URL。為了做到這一點,只需配置 [urlManager|CWebApplication::urlManager] 元件的[urlSuffix|CUrlManager::urlSuffix] 屬性為你所喜歡的後綴。 使用自定 URL 規則設置類別 ----------------------------- > 注意: Yii 從 1.1.8 版本起支援自定 URL 規則類別 預設情況下,每個 URL 規則都通過 [CUrlManager] 來宣告為一個 [CUrlRule] 物件,這個物件會解析當前請求並根據具體的規則來產生 URL。雖然 [CUrlRule]可以處理大部分 URL 格式,但在某些特殊情況下仍舊有改進餘地。 比如,在一個汽車銷售網站上,可能會需要支援類似 `/Manufacturer/Model` 這樣的 URL 格式,其中 `Manufacturer` 和 `Model` 都各自對應資料庫中的一個表。此時 [CUrlRule] 就無能為力了。 我們可以通過繼承 [CUrlRule] 的方式來創造一個新的 URL 規則類。並且使用這個類解析一個或者多個規則。以上面提到的汽車銷售網站為例,我們可以宣告下面的 URL 規則。 ~~~ [php] array( // 一個標準的 URL 規則,將 '/' 對應到 'site/index' '' => 'site/index', // 一個標準的 URL 規則,將 '/login' 對應到 'site/login', 等等 '<action:(login|logout|about)>' => 'site/<action>', // 一個自定 URL 規則,用來處理 '/Manufacturer/Model' array( 'class' => 'application.components.CarUrlRule', 'connectionID' => 'db', ), // 一個標準的 URL 規則,用來處理 'post/update' 等 '<controller:\w+>/<action:\w+>' => '<controller>/<action>', ), ~~~ 從以上可以看到,我們自定了一個 URL 規則類別 `CarUrlRule` 來處理類似 `/Manufacturer/Model` 這樣的 URL 規則。 這個類別可以這麼寫: ~~~ [php] class CarUrlRule extends CBaseUrlRule { public $connectionID = 'db'; public function createUrl($manager,$route,$params,$ampersand) { if ($route==='car/index') { if (isset($params['manufacturer'], $params['model'])) return $params['manufacturer'] . '/' . $params['model']; else if (isset($params['manufacturer'])) return $params['manufacturer']; } return false; // 這個規則沒套用 } public function parseUrl($manager,$request,$pathInfo,$rawPathInfo) { if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches)) { // 檢查 $matches[1] 和 $matches[3] 是否 // 在資料庫中有匹配的 manufacturer model // 如果有,設定 $_GET['manufacturer'] 和/或 $_GET['model'] // 並回傳 'car/index' } return false; // 這個規則沒套用 } } ~~~ 自定 URL 規則類別必須實現在 [CBaseUrlRule] 中定義的兩個介面。 * [CBaseUrlRule::createUrl()|createUrl()] * [CBaseUrlRule::parseUrl()|parseUrl()] 除了這種典型用法,自定 URL 規則類別還可以有其他的用途。比如,我們可以寫一個規則類來記錄有關 URL 解析和 UEL 建立的請求。這對於正在開發中的網站來說很有用。我們還可以寫一個規則類來在其他 URL 規則都匹配失敗的時候顯示一個自定 404 頁面。注意,這種用法要求規則類別在所有其他規則的最後宣告。 <div class="revision">$Id$</div>