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.

  3. 當使用者請求一個 URL,應用程式程式需要解析它變成可以理解的參數。

  4. 應用程式程式需求提供一種創造 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 後,有這些選項可供選擇:

使用命名的參數

一個規則可以和一些 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',
)

總之,當使用 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。前者與控制器名稱 postcomment 配對,而後者與動作名稱 createupdatedelete配對。你可以任意命名參數只要他們不與 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=adminlang=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 格式,其中 ManufacturerModel 都各自對應資料庫中的一個表。此時 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 頁面。注意,這種用法要求規則類別在所有其他規則的最後宣告。

$Id$