[譯]Asp.net MVC 之 Contorllers(二)


URL路由模塊

 書接上回[譯]Asp.net MVC 之 Contorllers(一)

URL 路由HTTP模塊通過獲取 URL,然后調用合適的執行方法處理進來的請求。URL 路由 HTTP 模塊取代了舊版本 ASP.NET 的 URL 重寫功能。URL 重寫的核心包括獲取請求、解析原始 URL 以及指導 HTTP 運行時環境服務於“可能相關但不同(possibly related but different)” 的 URL。

 

取代URL重寫

在可讀性、搜索引擎優化(SEO,search engine optimization)和程序處理 UR L的數量級之間做出權衡時,URL重寫就可以閃亮登場了。思考一下這個URL:

http://northwind.com/news.aspx?id=1234

news.aspx頁面集成了檢索信息、組織信息以及顯示信息的所有邏輯。這個 ID 是 Querystring 中的參數,根據他可以獲取特定的消息。對於程序猿,實現這個頁面,非常easy。

只需要簡單的三個步驟(相當於把大象裝冰箱):

  1. 獲取到 Query String 中參數 ID 值(打開冰箱門)。
  2. 運行一個查詢語句獲取結果(把大象放進去)。
  3. 創建一個展示的界面(關上冰箱門)。

對於用戶和搜索引擎來說,單純從URL很難真正的理解這個頁面是干什么的,而且很難記清楚網址以及具體需要傳的值。

URL重寫在兩個方面上做了改善:

首先、程序猿可以用一個通用的前端頁面(如:news.aspx)顯示相關內容。

其次、用戶可以請求更加友好的URL,這些URL被程序通過代碼自動映射成不那么直觀但又便於管理的URL上。

總的來說,URL 重寫就實現服務於請求的物理頁面與請求URL的解耦。

在 ASP.NET 4 Web Forms 最新版本中,可以使用 URL 路由將傳入的 URLs 匹配其他 URLs 而不會產生 HTTP 302 重定向的消耗。然而,在 ASP.NET MVC 中,URL 路由是把傳入的 URL 映射到 Controller 類和 Action 方法為目的的。

注 最初開發 URL 路由模塊的目的是作為一個 ASP.NET MVC 組件,現在已經是 ASP.NET 平台的一部分,只是 ASP.NET MVC 和 ASP.NET Web Forms 提供的 API 稍有不同。

 

路由請求

向IIS發出請求的時候,究竟發生了什么呢?

下圖給出了在 ASP.NET MVC 和 ASP.NET Web Forms 應用程序中涉及相關的各個步驟如何工作的總體圖。

 

URL路由模塊會攔截無法由IIS服務處理的應用程序的任何請求。如果URL是指向一個物理文件(例如,一個ASPX文件) ,那么路由模塊將忽略該請求,除非另行配置。在頁面處理程序方面,ASP.NET 機制正常處理該請求。

接着,URL路由模塊嘗試匹配所有應用程序定義的路由的URL。如果匹配到,請求將轉到 ASP.NET MVC 的領地,再調用一個控制器類進行處理。如果沒有匹配到,請求將被標准的 ASP.NET 運行時以最合適的方式處理,很可能的結果就是返回一個404。

最后,只有符合預定義的 URL 格式(也就是路由)的請求,才被允許享用 ASP.NET MVC 運行時的服務。所有這些請求都被路由到一個共同的序實例化控制器類的HTTP處理程,並調用其中定義的方法。接下來,控制器方法將選擇一個視圖組件,生成實際的響應。

 

URL路由模塊的內部結構

從實現角度講,我們應該注意到 URL 路由引擎是一個觸發 PostResolveRequestCache 事件的 HTTP 模塊。在 ASP.NET 緩存中先檢查,如果對於請求沒有可用的響應,之后就會觸發該事件。

HTTP 模塊匹配到用戶定義的 URL 路由請求的 URL,並將 HTTP 上下文設置為使用 ASP.NET MVC 標准的 HTTP 處理程序來處理該請求。作為程序猿,不可能直接處理 URL 路由模塊。該模塊由系統提供,不需要我們特別去配置。我們的責任是提供應用程序支持的路由,以及路由模塊實際使用的路由。

 

應用程序路由

按照設計,ASP.NET MVC 應用程序並沒有依賴於物理頁面。在 ASP.NET MVC 中,用戶請求代理資源(acting on resources)。然而,框架沒有規定描述資源和 Action 的語法。表達為"代理資源(acting on resources)",很可能會被認為是 REST。,當然,這樣想也不是很離譜。

可以使用 ASP.NET MVC 應用程序中的 REST 方式,ASP.NET MVC 面向他是松耦合的,ASP.NET MVC 承認他的概念,如資源和行為,我們可以隨意使用自己的語法來表達和實現資源和行動行為。例如,在一個純粹的 REST 解決方案,是使用H TTP 謂詞來表達行為動作(GET,POST,PUT和DELETE)和通過URL識別資源。可以在在 ASP.NET MVC 中實現一個純粹的 REST 的解決方案,但需要做一些額外的工作。

通過指定動作行為和資源可以自定義語法,在 ASP.NET MVC 中默認行為是使用自定義語法的 URLs。該語法是以 URL 模式的集合作為表現形式,也稱為路由。

 

URL模式和路由

路由是URL絕對路徑樣式匹配的字符串,也就是一個沒有協議、服務和端口信息的URL字符串。路由可能是一個字符串常量,但很可能還包含一些占位符。

一個簡單的路由:

/home/test

路由是一個常量字符串,並且他僅被一個路徑是 home/test 的 URL 匹配。然而,大多數時候,我們處理的是包含一個或多個占位符的參數化路由。

請看下面兩個例子:

/{resource}/{action}

/Customer/{action}

這兩個路由都可以被任何只有兩個部分的 URL 匹配。第二個要求第一段是字符串 “Customer”。然而,第一個沒有對每段內容做出具體限制。

通常,大括號{}內的占位符被稱為 URL 參數。只要 URL 參數是由常量或分隔符隔開,路由就可以有多個 URL 參數。正斜杠(/)字符作為路由各個部分之間的分隔符。占位符的名字(例如,action)是代碼在實際 URL 中檢索相應段的內容的關鍵。

下面是ASP.NET MVC 應用程序中默認路由:

{controller}/{action}/{id}

上面路由包含三個占位符,其中由分隔符分開。下面是一個匹配上面路由的一個URL:

/Customers/Edit/ABC

我們可以添加多個路由,並且給路由添加多個占位符,也可以刪除該默認路由。

 

定義應用程序路由

應用程序的路由通常注冊在global.asax文件中,他在應用程序啟動時被處理。

global.asax文件中處理路由的部分:

    public class MvcApplication : HttpApplication
    {
        protected void Application_Start()
        {
            ...
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            ...
        }
    }
MvcApplication

 

RegisterRoutes是RouteConfig類中的一個方法,RouteConfig類一般定義在App_Start文件夾中。當然,我們可以隨意命名。下面是這個類的具體實現:

    public class RouteConfig
    {
        public static void  RegisterRoutes(RouteCollection routes)
        {
            ...

            // 路由集合 
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
RouteConfig

 

Application_Start事件處理程序調用了包含所有路由集合的方法 RegisterRoutes。關於 RegisterRoutes 方法的名稱這里需要注意一下,他不是死的的,只要認為合適,就可以隨意更改他。

為了支持路由,必須添加一個被 ASP.NET 管理的靜態路由對象集合。這個集合就是 RouteTable.Routes。通常我們使用 MapRoute 方法來填充這個集合。MapRoute 方法有很多重載方法,大部分時候他們都是行之有效的。然而,他不會讓我們配置路由對象每一個可以配置的方面。如果我們需要設置一個路由,但 MapRoute 不支持,那么我們可能要采取下面的代碼:

            // 創建一個新的路由,並添加到集合中
            var route = new Route(...);
            RouteTable.Routes.Add("NameOfTheRoute", route);
NewRoute

 

路由的特點是幾個屬性(名稱,URL格式,默認值,約束,數據標記和路由處理)。我們最常設置的屬性基本就是名稱、 URL 模式和默認值。

展開默認路由方法的代碼:

            // 路由集合 
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
MapRoute

 

第一個參數是路由的名字,每一個路由都應該有一個唯一的名字。第二個參數是路由模式/格式。第三參數是默認路由參數值的對象。

注意,UR L甚至可以使用非完整格式匹配的模式。讓我們思考下這個 URL,HTTP:// yourserver.com。乍一看,這個URL不會被路由匹配。但是,如果URL參數有默認值,那么默認值得部分被認為是可選的。因此,對於前面的例子,當請求根 URL 的時候,該請求就是調用 Home 控制器上的 Index 方法。

 

處理路由

當嘗試匹配一個請求 URL 到定義的路由的時候,ASP.NET URL 路由模塊采用了一些規則。最重要的規則是,路由必須按照定義在 Global.asax 中的順序進行檢查。

為了確保路由是按照正確的順序被處理,必須按照具體性遞減的順序。不管怎么樣,我們必須注意, 路由匹配是在整個路由集合中一直嘗試匹配。 當匹配到以后,將停止匹配,不會繼續去匹配更多的路由。這樣的話,把新添加的路由放在路由列表的尾部,可能不會起作用,也可能引起一些麻煩。另外還要注意,如果在列表的頂部放置一個可以捕捉全部格式的路由,那么,所有其他的路由將會被全部忽略。

注:路由順序是小事小的不值得一體,但他影響卻能大的無法想象。

在不考慮匹配順序的情況下,其他還有什么可能影響匹配URL路由處理的因素。如前所述,提供路由的默認值。如果請求缺少參數,則會使用默認值,默認值就是簡單地自動分配給定義的占位符。思考下面兩個路由:

{Orders}/{Year}/{Month}{Orders}/{Year}

如果賦值給第一個路由中{Year}和{Month},那么由於默認值的原因第二個路由將永遠不會被匹配到,無論是否指定具體年和月,第一個路由總是被成功匹配。

而結尾的斜線(/)也是一個陷阱。路由{Orders}/{Year} 和{Orders}/{Year}/ 兩個完全不是一回事。

另一個影響URL路由匹配的因素是約束列表,我們可以選擇給路由定義。路由約束就是要求給定的 URL 參數必須遵循的 URL 匹配路由的附加條件。URL 不僅應與 URL 模式兼容,它也需要包含兼容的數據。一個約束可以以各種方式來定義,其中包括通過正則表達式。

帶約束路由的例子:

            // 帶約束的路由集合
            routes.MapRoute(
                name: "ProductInfo",
                url: "{controller}/{productId}/{locale}",
                defaults: new {controller = "Product", action = "Index", locale = "en-us"},
                constraints: new {productId = @"\d{8}", locale = "[a-z]{2}-[a-z]{2}"});
MapRouteWithConstraints

 

特別指出,該路由要求 PRODUCTID 占位符必須是正好8個數字的數字序列,而 local 的占位符必須是用破折號分開的一對雙字母字符串。約束不能保證所有無效的產品 ID 和 local 代碼都被攔截,但至少做了大量的攔截工作。

 

路由處理程序

路由定義了一系列最低要求的規則,根據路由模塊決定請求的URL是否可以訪問應用程序。最終決定如何重新映射所請求的 URL 完全是另一個組件。這就本段的主角路由處理程序。路由處理程序是處理匹配給定路由任何請求的對象。它存在的唯一目的是返回 HTTP 處理程序,該 HTTP 處理程序將服務於任何匹配的請求。

從技術上講,路由處理程序是一個實現了IRouteHandler的類,接口定義如下:

        public interface IRouteHandler
        {
            IHttpHandler GetHttpHandler(RequestContext requestContext);
        }
IRouteHandler

 

定義在System.Web.Routing命名空間下,RequestContext封裝了請求 HTTP 上下文,再加上所有可用的具體路由的信息,如路由對象本身、URL參數以及約束。這些數據是被分組到一個RouteData對象。下面是RequestContext類的簽名:

public class RequestContext
{
    public RequestContext(HttpContextBase httpContext, RouteData routeData);

    public HttpContextBase HttpContext { get; set; }

    public RouteData RouteData { get; set; }
}
RequestContext

 

ASP.NET MVC 框架並沒有提供很多內置的路由處理程序,而這可能是一個需要使用自定義的並非公用的路由處理程序的簽名。然而,在需要的情況下,可以利用這個擴展功能。會在后面的章節說自定義路由處理程序時,並提供一個例子。

 

處理物理文件請求

路由系統是否具有處理匹配物理文件的請求,是有助於建立一個成功的URL到路由匹配的路由系統的另一個配置方面。

默認情況下,ASP.NET 路由系統忽略可以被映射到存在於服務器上的物理文件的 URL 請求。需要注意,如果請求文件在服務器真實存在,即便與路由請求相匹配,路由系統仍然會忽略該請求。

如果需要匹配物理文件,可以通過設置路由來強制路由系統處理所有請求,在 RouteCollection 對象中將 RouteExistingFiles屬性設置為 True,如下所示:

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.RouteExistingFiles = true;
        }
RouteExistingFiles

 

注意:通過路由來處理所有的請求,可能會在 ASP.NET MVC 應用程序中產生一些問題。例如,將前面的代碼添加到一個 ASP.NET MVC 應用程序的 Global.asax.cs 文件,然后運行,當訪問default.aspx頁面時,會出現一個 HTTP 404 錯誤。

 

防止路由定義的URL

在ASP.NET URL路由模塊沒有限制我們去維護可接受的URL模式列表;我們也可以保留某些 URL 關閉的路由機制。

我們可以通過兩個步驟來防止來自於處理某些URL的路由系統。

首先,為這些URL定義模式並保存到的路由中。

接下來,將該路由鏈接到一個特殊的路由處理程序 StopRoutingHandler 類。它所做的就是調用GetHttpHandler方法時拋出一個NotSupported異常。

例如,下面的代碼說明路由系統將忽略任何axd的請求:

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        }
IgnoreRoute

 

所有 IgnoreRoute 的確關聯到 StopRoutingHandler 路由處理程,來處理路由構建指定的URL模式。

最后,解釋一下請求URL中的{ * PathInfo}的占位符。標記 PATHINFO 僅僅代表 URL 中在 .axd 后面的所有內容的一個占位符。星號( * ) 表示最后的參數應該匹配 UR L的其余部分。換句話說,任何跟隨在 .axd 后面的字符串都是 PATHINFO 參數匹配的范圍。這些參數被稱為完全捕獲參數。

 

屬性路由

NuGet 的 ASP.NET MVC 5 中包含 AttributeRouting 。屬性路由是所有有關使用屬性直接在控制器的Action方法上定義的路由。正如前面所說,經典路由是在應用程序啟動時基於在Global.asax中建立的約定。

任何時候的請求,URL都是與路由注冊的模板中相匹配的。如果匹配,就可以確定請求的相應的控制器和action方法。如果沒匹配,該請求將被拒絕,結果通常是404 消息。現在,在大型應用程序中,甚至是在具有很強的REST特點的中型應用中,路由的數量可能相當可觀,隨隨便便就會定義出上百個路由。可能很快就會發現,經典路由變得有些力不從心。出於這個原因,AttributeRouting項目已啟動,現已集成在ASP.NET MVC 5 中,甚至在 Web API 也有集成,將在后面章節討論。

        [HttpGet("orders/{orderId}/show")]
        public ActionResult GetOrderById(int orderId)
        {
            ...
        }
AttributrRoute

 

上面代碼設置方法 GetOrderById 在通過 HTTP GET 調用,並且 URL 模板匹配指定模式時,該方法是可以被訪問的。路由參數(orderId 標記) 必須與定義在方法簽名中一個參數相匹配。有更多的屬相可用(對於每個 HTTP 謂詞),但這已經是路由屬性要點。了解更多信息(如,配置),可以參考http://attributerouting.net,在 NuGe t包中已經集成到 ASP.NET MVC。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM