URL 網址管理
網路應用程式完整的 URL 管理包括兩個方面:
- When a user request comes in terms of a URL, the application needs to parse it into understandable parameters.
The application needs to provide a way of creating URLs so that the created URLs can be understood by the application.
當使用者請求一個 URL,應用程式程式需要解析它變成可以理解的參數。
- 應用程式程式需求提供一種創造 URL 的方法,以便建立的 URL 應用程式程式可以理解的。
對於Yii應用程式程式,這些透過 CUrlManager 輔助完成。
建立 URL
雖然URL可被寫死在控制器的視圖文件,但往往可以很靈活地動態建立它們:
$url=$this->createUrl($route,$params);
$this
指的是控制器實體;$route
指定請求的 route 的要求;$params
列出了附加在網址中的 GET
參數。
預設情況下,URL 以 get
格式使用 createUrl 建立。例如,提供 $route='post/read'
和 $params=array('id'=>100)
,我們將獲得以下網址:
/index.php?r=post/read&id=100
參數以一系列 Name=Value
通過符號串聯起來出現在請求字串,r
參數指的是請求的 route。這種 URL 格式使用者友善不是很好,因為它需要一些非字字符。
我們可以使上述網址看起來更簡潔,更不言自明,通過採用所謂的 path
格式,省去查詢字串和把 GET 參數加到路徑訊息,作為網址的一部分:
/index.php/post/read/id/100
要更改 URL 格式,我們應該配置 urlManager 應用程式元件,以便 createUrl 可以自動切換到新格式和應用程式程式可以正確理解新的網址:
array( ...... 'components'=>array( ...... 'urlManager'=>array( 'urlFormat'=>'path', ), ), );
請注意,我們不需要指定的 urlManager 元件的類別,因為它在 CWebApplication 事先宣告為CUrlManager。
createurl 方法所產生的是一個相對地址。為了得到一個絕對的 url ,我們可以用前綴yii">
提示:此網址通過 createurl 方法所產生的是一個相對地址。為了得到一個絕對的 Url ,我們可以用前綴
yii:
:app()->hostInfo,或調用 createAbsoluteUrl。
使用者友善的 URL
當用 path
格式 URL,我們可以指定某些 URL 規則使我們的網址更使用者友善。例如,我們可以產生一個短短的 URL /post/100
,而不是冗長 /index.php/post/read/id/100
。網址建立和解析都是通過 CUrlManager 指定網址規則。
要指定的URL規則,我們必須設定 urlManager 應用程式元件的屬性 rules:
array( ...... 'components'=>array( ...... 'urlManager'=>array( 'urlFormat'=>'path', 'rules'=>array( 'pattern1'=>'route1', 'pattern2'=>'route2', 'pattern3'=>'route3', ), ), ), );
這些規則被指定成一系列的路由模式配對陣列,每配對對應於一個單一的規則。路由的格式必須是有效的正則表達式,沒有分隔符和修飾語。它是用於匹配網址的路徑訊息部分。還有 route應指向一個有效的路由控制器。
除此之外,一個規則可以指定客製化的選項。如下所示:
'pattern1'=>array('route1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)
上述,該陣列包含了一些客製化的選項。版本 1.1.0 後,有這些選項可供選擇:
urlSuffix: URL 規則的後綴。預設是 null,表示使用 CUrlManager::urlSuffix 的值。
caseSensitive: 是否大小寫敏感。預設是 null,表示使用 CUrlManager::caseSensitive 的值。
defaultParams: 預設的 GET 參數 (name=>value) 規則。當這個規則被用來剖析傳入的需求時,這屬性的值將會被置入 $_GET。
matchValue: 當建立的 URL 時,GET 參數的值是否與規則的子樣式匹配。預設是空值,代表使用 CUrlManager::matchValue 的值。如果這個屬性是 false,代表一個規則會被用來建立 URL ,如果他的路由和給定的參數相互對應。如果這個屬性是 true,納給定的參數值必須和子模式的參數相對應。注意一點,將這個屬性設為 true 會降低效能。
使用命名的參數
一個規則可以和一些 GET 參數相關聯。這些 GET 參數會以特殊的符號格式出現在規則的樣式裡,如下所示:
<ParamName:ParamPattern>
ParamName
表示 GET 參數名字,可選項 ParamPattern
表示將用於匹配 GET 參數值的正則表達式。當產生一個 URL 時,這些參數符號將被相應的參數值替換;當解析一個網址時,相應的 GET 參數將通過解析結果來產生。
我們使用一些例子來解釋網址工作規則。我們假設我們的規則包括如下三個:
array( 'posts'=>'post/list', 'post/<id:\d+>'=>'post/read', 'post/<year:\d{4}>/<title>'=>'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 產生網址,路由和傳遞給該方法的 GET 參數被用來決定哪些網址規則適用。如果關聯規則中的每個參數可以在 GET 參數找到的,將被傳遞給 createUrl,如果路由的規則也匹配路由參數,規則將用來產生網址。
如果 GET 參數傳遞到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
獲得)。
createurl 方法所產生的是一個相對地址。為了得到一個絕對的 url ,我們可以用前綴yii">
註:使用的URL規則將降低應用程式的性能。這是因為當解析請求的 URL, CUrlManager 嘗試使用每個規則來匹配它,直到某個規則可以適用。因此,高流量網站應用程式應盡量減少其使用的 URL 規則。
參數化路由
我們或許會在一個規則的路由的部分參照命名參數。這將允許一個規則被用在許多路由匹配標準。他也會幫助減少一個應用程式需要的規則數量和增進整體的效能。
我們使用如下的範例規則來描述如何使用命名參數來參數化路由:
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 例如:
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 應用程式程式元件。
我們首先需要配置網路伺服器,這樣一個 URL 沒有入口腳本仍然可以處理入口腳本。如果是 Apache HTTP server,可以通過打開網址重寫引擎和指定一些重寫規則。這兩個操作可以在包含入口腳本的目錄下的 .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 元件的 showScriptName 屬性為 false
。
現在,如果我們調用 $this->createUrl('post/read',array('id'=>100))
,我們將取得網址 /post/100
。更重要的是,這個URL可以被我們的網路應用程式程式正確解析。
偉造的 URL 後綴
我們還可以增加一些網址的後綴。例如,我們可以用 /post/100.html
來替代 /post/100
。這使得它看起來更像一個靜態網頁 URL。為了做到這一點,只需配置 urlManager 元件的urlSuffix 屬性為你所喜歡的後綴。
使用自定 URL 規則設置類別
注意: Yii 從 1.1.8 版本起支援自定 URL 規則類別
預設情況下,每個 URL 規則都通過 CUrlManager 來宣告為一個 CUrlRule 物件,這個物件會解析當前請求並根據具體的規則來產生 URL。雖然 CUrlRule可以處理大部分 URL 格式,但在某些特殊情況下仍舊有改進餘地。
比如,在一個汽車銷售網站上,可能會需要支援類似 /Manufacturer/Model
這樣的 URL 格式,其中 Manufacturer
和 Model
都各自對應資料庫中的一個表。此時 CUrlRule 就無能為力了。
我們可以通過繼承 CUrlRule 的方式來創造一個新的 URL 規則類。並且使用這個類解析一個或者多個規則。以上面提到的汽車銷售網站為例,我們可以宣告下面的 URL 規則。
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 規則。
這個類別可以這麼寫:
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 中定義的兩個介面。
除了這種典型用法,自定 URL 規則類別還可以有其他的用途。比如,我們可以寫一個規則類來記錄有關 URL 解析和 UEL 建立的請求。這對於正在開發中的網站來說很有用。我們還可以寫一個規則類來在其他 URL 規則都匹配失敗的時候顯示一個自定 404 頁面。注意,這種用法要求規則類別在所有其他規則的最後宣告。