天天看點

控制器 - 應用路由(二)

從設計角度出發,一個 ASP.NET 應用程式并非強制地依賴實體頁面。在 ASP.NET MVC 中,使用者送出請求并在某個資源上做相應處理。然而,就整個架構而言,并非授權這種文法來描述資源和行為。我深信這樣的表述使你很可能地想到了 REST(Representational State Transfer)。

雖然你可以在一個 ASP.NET MVC 應用中使用純粹的 REST 方案, 但是我想說的是ASP.NET 是松散的面向 REST,它承認例如資源和行為的概念。舉個例子,在一個純 REST 解決方案中,你會使用 HTTP 動詞來表示對應的行為 - GET,POST,PUT 和 DELETE,并使用 URL 來定位資源。在 ASP.NET 中實作一個純 REST 解決方案是可行的,但是你需要做額外的工作。

ASP.NET MVC 的預設行為是使用自定義的 URL,并且路由到哪些行為和資源,這都是由你負責的。這些文法形式一系列 URL 模式集合表達的,也被稱為路由。

URL 模式和路由

一個路由就是一個模式比對字元串,換句話說就是通過它來比對一個 URL 的絕對路徑,URL 字元串是不包含協定,伺服器,和端口資訊的。一個路由是一個常量字元串,但是它往往也包含一些占位符。底下就是一個例子:

/home/test

這個路由是一個常量字元串,而且僅能比對 URL 為 /home/test 的路由。絕大多數的情況下,我們更多的是處理那些包含一個或多個占位符的路由。這裡有兩個例子:

/{resource}/{action}

/Customer/{action}

這兩個路由僅會比對那些包含兩個片段的 URL。後者僅會比對首個片段字元串為“Customer”的 URL。二前者,并沒有對片段設定相應的要求。

占位符通常被稱作 URL 參數,它是由{}包裹的。你可以在一個路由中添加多個占位符,隻要它們之間被常量或者分隔符分開。斜杠/在路由的多個片段之間充當分隔符。占位符的名稱(比如 action)就是鍵,通過它,你可以在代碼中通過程式設計的方式擷取對應片段中的值。

這就是 ASP.NET MVC 應用程式的預設路由:

{controller}/{action}/{id}

在這個路由中,包含了三個占位符,并且被斜杠所分割開。而底下的 URL 就可以比對這個路由:

/Customers/Edit/ALFKI

你可以添加任意多的路由,在路由中也可以添加任意多的占位符。同樣,你也可以删除程式預設的路由。

定義應用路由

一個應用程式的路由通常定義在 global.asax 檔案中,而且他們是在程式啟動的時候被處理。現在,就看一下 global.asax 處理路由的代碼片段:

RegisterRoutes 是 RouteConfig 類中的一個方法,這個類被定義在項目中其他的檔案夾下。通常這個檔案夾叫 App_Start (你也可以随意定義這個檔案夾的名稱)。底下就是這個類的定義:

通過上面代碼可以看到,Application_Start 事件處理器通過調用 RegisterRoutes 公共靜态方法來注冊所有路由。注意到 RegisterRoutes 方法的名稱和原型是随意的,是以如果你有充分的理由,你就可以修改它。

應用所支援的路由必須注冊到 Route 對象的一個靜态集合中,它是由 ASP.NET MVC 管理,這個集合就是 RouteTable.Routes。你一般通過 MapRoute 方法來建立路由集合。MapRoute  方法提供了許多重載,并且多數情況上都已夠用。但是,這個方法并不會讓你配置一個路由對象的各個方面。如果你想在一個路由對象上設定一些内容但是 MapRoute 方法并不提供,你可能就會求助于底下的代碼了:

一個路由通常包含一些屬性,比如名稱,URL 模式,預設值,限制,資料标記,和路由處理器。而最常設定的屬性包括名稱,URL 模式,和預設值。現在讓我們再來回顧一下預設路由:

第一個參數是路由的名稱,每一個路由都應該有一個獨有的名稱。第二個參數是 URL 模式。第三個參數是一個對象,用來設定預設值的。

處理路由

ASP.NET URL routing module 應用了一系列規則将入站請求 URL 與一個定義好的路由進行比對。最重要的一個規則就是,程式會按照路由在 global.asax 注冊的順序進行檢查比對。

為了確定路由在正确的順序下被處理,你必須将他們按最可能的情況到最不可能的情況進行排序。但是不管在什麼情況下,搜尋一個比對路由的過程總是在找到第一個比對的路由時結束。這就表示,如果在路由清單的最後添加一個新的路由可能并不奏效,或者将會産生一些錯誤。另外,要清楚在清單頂部放置的 catch-all 模式将會使特定格式的路由被忽略掉。

除了外觀上顯而易見的排序外,也有其他的因素幹擾比對路由的過程。正如之前提到的,為路由提供的預設值。預設值會被自動指派到定義好的占位符上,如果目前 URL 沒有提供相應的值。考慮以下的兩個路由:

{Orders}/{Year}/{Month}

{Orders}/{Year}

在第一個路由上,你為{Year}和{Month}兩個占位符設定了預設值,那麼第二個路由将永遠不會被比對到,這是由于設定了預設值而導緻的。第一個路由将會永遠被比對到不管 URL 是否已經指定了一個年份或者月份。

斜杠也是一個陷阱。{Orders}/{Year}和{Orders}/{Year}/是兩個完全不同的路由,其中一個永遠不會比對另一個。

另一個影響路由比對的因素就是可選屬性 - 限制。一個路由限制就強制路由參數必須滿足限制要求,不然就無法比對。URL 不僅需要與路由模式相适應,而且也要包含滿足限制要求的資料。一個路由限制可以通過多種方法來定義,包括通過使用正規表達式。這裡就有一個限制的例子:

是以,這個路由的 productId 占位符必須是八位數的數字,而 locale 占位符必須是一對由連字元連接配接而成的兩個字元的字元串。限制并不能保證将所有無效的産品編号和本地代碼攔截在門外,但是它至少削減了許多額外的工作。

路由處理器

根據 routing module 決定入站請求 URL 是否被應用所接受,路由僅定義了一小部分規則。而最終決定怎麼重新映射請求進來的 URL 的元件就是 route handle。路由處理器就是一個處理所有比對過的請求的對象。它的唯一目标就是傳回真正處理請求的 HTTP handler。

技術上講,一個路由處理器就是一個實作了 IRouteHandler 接口的類。這個接口定義如下:

RequestContext 類定義在 System.Web.Routing 命名空間下,它封裝了請求的 HTTP 上下文和一些與路由相關的可用資訊,比如 Route 對象本身, URL 參數,和限制。這些資料被封裝在一個 RouteData 對象裡。底下就是 RequestContext 類的聲明:

ASP.NET MVC 架構并沒有提供太多内置的路由處理器,這也就暗示了需要自定義的路由處理器并不常見。但是,如果需要,你可以利用它,過會,我們還會回到自定義路由處理器上并在後面章節上介紹一個例子。

為實體檔案處理請求

在路由系統中,另一個可配置的方面并且可能會影響到是否能夠成功比對的方面就是路由系統是否需要處理一個能顧比對實體檔案的路由請求。

預設情況下,ASP.NET 路由系統會忽略那些可以比對到實體檔案的請求。注意,如果這個伺服器檔案存在,路由系統會忽略目前請求即使這個請求可以比對一個路由。

如果你需要的話,你可以強制路由系統處理所有請求,隻要設定 RouteCollection 對象的 RouteExistingFiles 屬性為 true 即可。代碼如下:

注意,在一個 ASP.NET MVC 應用程式中,如果所有請求都通過路由處理可能會産生一些問題。比如,如果你在一個簡單的 ASP.NET MVC 應用中的 global.asax.cs 檔案中添加之前的代碼,并且運作程式,如果你通路 default.aspx 頁面,就會立即收到 HTTP 404 錯誤。

阻止路由定義好的 URL

ASP.NET URL routing module 并沒有限制你維護一堆可接受的 URL 模式。你也可以将某些特定的 URL 排除在路由機制之外。你可以通過兩部方法來阻止路由系統來處理特定的 URL。第一,你為這些 URL 定義一個模式,并将它儲存在一個路由對象中。第二,你将這個路由對象與一個特殊的路由處理器相連,這個路由處理器就是 StopRoutingHandler 類。它所做的就是當它的 GetHttpHandler 方法被調用的時候抛出一個 NotSupported 異常。

比如,底下的代碼指導路由系統忽略所有 .axd 請求:

所有的 IgnoreRoute 方法都與 StopRoutingHandler 路由處理器相關聯。

最後,為 URL 中的 {*pathInfo} 占位符做一點解釋是有必要的。pathInfo 記号表示任意跟着 .axd 後面的内容。而星号表示最後的參數應該比對 URL 的剩餘部分。換句話說,任何緊跟着 .axd 擴充的内容都将進入 pathInfo 參數中。這個參數也被認為是 catch-all 參數。

屬性路由

任何時候如果有請求進來,URL 會與注冊的路由模闆進行比對。如果一個路由比對成功,那麼處理這個請求的控制器和當中的方法就被确定下來了。如果沒有比對成功,那麼請求就會被拒絕,并且會産生一個 404 錯誤消息。但是現在,在大型應用中,或者在一個具有濃濃 REST 風味的中型項目中,路由的數量可能會非常多,并且這樣的路由記錄可能就有上百條。為此,屬性路由應運而生,并且現在已經內建到 ASP.NET MVC 5 中,甚至在 Web API 中。我們将會在後面詳細讨論。

【聲明:本文是個人翻譯而來。當中可能會存在許多不當之處,萬望指出,謝謝。文章會持續更新】

繼續閱讀