Asp.Net MVC 路由

來源:互聯網
上載者:User

 

來自  張子陽 http://www.cnblogs.com/JimmyZhang/archive/2009/03/08/1406512.html

原文連結:http://www.asp.net/learn/mvc/

在這篇教程中,我將為你介紹每個ASP.NET MVC應用程式都具有的一個重要功能,稱作ASP.NET路由(ASP.NET Routing)。ASP.NET路由模組負責將即將到來的瀏覽器請求映射到特定的MVC控制器動作。學完這篇教程之後,你將會理解標準的路由表是如何將請求映射到控制器動作的。

1. 理解預設路由表

當你建立一個新的ASP.NET MVC應用程式時,應用程式已經被配置為使用ASP.NET路由。ASP.NET路由在兩個地方設定。

第一點,在你的應用程式Web設定檔(Web.config檔案)中啟用ASP.NET路由。在設定檔中有四個節點與路由有關:sytem.web.httpModules節,system.web.httpHandlers節,system.webserver.modules節,以及system.webserver.handlers節。特別要小心不要刪除了這些節點,因為沒有它們路由將不能工作。

第二點,也是更為重要的一點,在應用程式的Global.asax檔案中建立了一個路由表。Global.asax檔案是一個特殊的檔案,它包含了作用於ASP.NET應用程式生命週期事件的事件處理常式。路由表在Application Start事件期間建立。

代碼清單1中的檔案包含了一個ASP.NET MVC應用程式的預設Global.asax檔案。

代碼清單1 - Global.asax.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Web.Routing;
namespace MvcApplication1 {

    // Note: For instructions on enabling IIS6 or IIS7 classic mode,

    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication

    {

        public static void RegisterRoutes(RouteCollection routes)

        {

            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(

                "Default",             // Route name

                "{controller}/{action}/{id}",                           // URL with parameters

                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults             );
        }
        protected void Application_Start()

        {

            RegisterRoutes(RouteTable.Routes);

        }

    }

}

當一個MVC應用程式首次運行時,會調用Application_Start()方法。這個方法隨後調用了RegisterRoutes()方法。RegisterRoutes()方法建立了路由表。

預設的路由表包含了一個路由(名叫Default)。Default路由將URL的第一部分映射到控制器名,URL的第二部分映射到控制器動作,第三個部分映射到一個叫做id的參數。

假設你在瀏覽器的地址欄輸入了下面的URL:

/Home/Index/3

預設的路由將這個URL映射為下面的參數:

Controller = Home

Action = Index

id = 3

當你請求URL /Home/Index/3時,將會執行下面的代碼:

HomeController.Index(3)

Default路由包含了所有三個參數的預設值。如果你不提供控制器,那麼控制器參數預設值為Home。如果你不提供動作,動作參數預設為值Index。最後,如果你不提供id,id參數預設為空白字串。

讓我們看看幾個例子,Default路由是如何將URL映射到控制器動作的。設想你在瀏覽器地址欄輸入了下面的URL:

/Home

由於Default路由參數的預設值,輸入這個URL將會調用代碼清單2中的HomeController類的Index()方法。

代碼清單2 - HomeController.cs

using System.Web.Mvc;
namespace MvcApplication1.Controllers {

    [HandleError]

    public class HomeController : Controller

    {

        public ActionResult Index(string id)

        {

            return View();

        }

    }

}

在代碼清單2中,HomeController類包含了一個叫做Index()的方法,它接受一個叫做Id的參數。URL /Home將會導致調用Index()方法,並使用Null 字元串作為Id參數的值。

出於MVC架構調用控制器動作的方式,URL /Home也符合代碼清單3中HomeController類的Index()方法。

代碼清單3 - HomeController.cs(不含參數的Index動作)

using System.Web.Mvc;
namespace MvcApplication1.Controllers {

    [HandleError]

    public class HomeController : Controller

    {  

       public ActionResult Index()

        {

            return View();

        }

    }

}

代碼清單3中的Index()方法不接受任何的參數。URL /Home將會導致調用這個Index()方法。URL /Home/Index/3也會調用這個方法(Id被忽略)。

URL /Home也會符合代碼清單4中HomeController類的Index()方法。

代碼清單4 - HomeController.cs(使用可空參數的Index動作)

using System.Web.Mvc;
namespace MvcApplication1.Controllers {

    [HandleError]

    public class HomeController : Controller

    {

        public ActionResult Index(int? id)

        {

            return View();  

       }

    }

}

在代碼清單4中,Index()方法擁有一個整數參數。因為這個參數是一個可空參數(可以擁有Null值),因此可以調用Index()而不會引發錯誤。

最後,使用URL /Home 調用代碼清單5中的Index()方法將會引發一個異常,因為Id參數並非一個可空參數。如果你試圖調用Index()方法,那麼你將會獲得一個圖1中所示的錯誤。

代碼清單5 - HomeController.cs(含有Id參數的Index動作)

using System.Web.Mvc;
namespace MvcApplication1.Controllers {

    [HandleError]

    public class HomeController : Controller

    {

        public ActionResult Index(int id)

        {

            return View();

        }

    }

}

圖01: 調用一個期望參數值的控制器動作

另一方面,URL /Home/Index/3 能夠與代碼清單5中的Index控制器動作很好地工作。/Home/Index/3請求將會引發調用含有一個Id的Index()方法,且該Id值為3.

這篇教程的目的是為你提供一個ASP.NET路由的簡短介紹。我們仔細查看了預設的路由表,它在你建立新的ASP.NET MVC應用程式時獲得。你學習了預設的路由表如何將URL映射到控制器動作。

2.建立自訂路由

在這篇教程中,你會學習到如何為ASP.NET MVC應用程式添加自訂路由。你會學習如何將Global.asax檔案中的預設路由表修改為自訂路由。

對於簡單的ASP.NET MVC應用程式,預設的路由表已經可以很好的完成工作了。然而,你可能發現會存在特定的路由需求。在這種情況下,你可以建立一個自訂路由。

設想一下,舉個例子,你正在建立一個部落格應用程式。你可能想要像這樣處理即將到來的請求:

/Archive/12-25-2009

當使用者輸入這一請求,你想要返回對應於日期12/25/2009的部落格條目。為了處理這種類型的請求,你需要建立一個自訂路由。

代碼清單1中的Global.asax包含了一個新的自訂路由,命名為了Blog,它處理了類似/Archive/條目日期 這樣的請求。

代碼清單1 - Global.asax(含有自訂路由)

using System.Web.Mvc; using System.Web.Routing;
namespace MvcApplication1 {
    public class MvcApplication : System.Web.HttpApplication

    {

        public static void RegisterRoutes(RouteCollection routes)

        {

            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(

                "Blog",                                           // Route name

                "Archive/{entryDate}",                            // URL with parameters

                new {controller = "Archive", action = "Entry" }  // Parameter defaults             );
            routes.MapRoute(

                "Default",                                              // Route name   

              "{controller}/{action}/{id}",                           // URL with parameters

                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults             );
        }
        protected void Application_Start()

        {

            RegisterRoutes(RouteTable.Routes);

        }

    }

}

添加到路由表中的路由順序非常重要。我們的新自訂Blog路由在現有的Default路由前面。如果你將這個順序顛倒過來,那麼Default路由將總是被調用,而不是自訂路由。

自訂Blog路由匹配任何以/Archive/作為開始的請求。因此,它匹配所有下面的URL:

/Archive/12-25-2009

/Archive/10-6-2004

/Archive/apple

自訂路由將即將到來的請求映射到名為Archive的控制器,並且調用了Entry()動作。當調用Entry()方法時,條目日期作為entryDate參數進行了傳遞。

代碼清單2 - ArchiveController.cs

using System;

using System.Web.Mvc;
namespace MvcApplication1.Controllers {

    public class ArchiveController : Controller

    {
        public string Entry(DateTime entryDate)

        {

            return "You requested the entry from " + entryDate.ToString();

        }
    }

}

注意到代碼清單2中的Entry()方法接受一個DateTime類型的參數。MVC架構非常的聰明,足以自動地將URL中的條目日期轉換為DateTime值。如果URL中的條目日期參數不能轉換為DateTime,將會引發錯誤(1)。

圖1 - 轉換參數時的錯誤

這篇教程的目的是示範如何建立自訂路由。你學習了如何在Global.asax檔案的路由表中添加自訂路由,該路由代表著部落格條目。我們討論了如何將對部落格條目的請求映射到名為ArchiveController的控制器,和名為Entry()的控制器動作上。

3.建立路由約束

你可以使用路由約束來限制匹配特定路由的瀏覽器請求。可以使用Regex來指定一個路由約束。

舉個例子,假設你已經在Global.asax檔案中定義了一個路由。

代碼清單1 - Global.asax.cs

routes.MapRoute(     "Product",     "Product/{productId}",

    new {controller="Product", action="Details"} );

代碼清單1包含一個叫做Product的路由。你可以使用Product路由將瀏覽器請求映射到代碼清單2中的ProductController。

代碼清單2 - ControllersProductController.cs

using System.Web.Mvc;
namespace MvcApplication1.Controllers {

    public class ProductController : Controller

    {

        public ActionResult Details(int productId)

        {

            return View();

        }

    }

}

注意到Product控制器公布的Details()動作接受一個叫做productId的參數。這個參數是一個整數參數。

定義在代碼清單1中的路由將會匹配下面的任意URL:

  • /Product/23
  • /Product/7

不幸的是,路由也會匹配下面的URL:

  • /Product/blah
  • /Product/apple

因為Details()動作期望的是一個整數值,發起一個含有非整數值的請求將會導致錯誤。舉個例子,如果你在瀏覽器中輸入/Product/apple URL,那麼你將會得到圖1所示的錯誤頁。

圖1:錯誤頁

你實際想做的是只匹配包含合適整數productId的URL。當定義路由來限制與路由相匹配的URL時,你可以使用約束。代碼3中的修改後的Product路由包含了一個Regex,它限制了只匹配數字。

代碼清單3 - Global.asax.cs

routes.MapRoute(     "Product",     "Product/{productId}",     new {controller="Product", action="Details"},     new {productId = @"d+" } );

Regexd+匹配一個或多個整數。這個限制使得Product路由匹配了下面的URL:

  • /Product/3
  • /Product/8999

但是不匹配下面的URL:

  • /Product/apple
  • /Product

這些瀏覽器請求將由另外的路由處理,或者,如果沒有匹配的路由,將會返回一個“The resource could not be found”錯誤。

4. 建立一個自訂路由約束

這篇教程的目標是示範如何建立一個自訂路由約束。自訂路由約束允許你阻止某個路徑被匹配,除非滿足一些自訂的條件。

在這篇教程中,我們建立了一個Localhost路由約束。Localhost路由約束只匹配本機電腦發出的請求。通過互連網發出的遠程請求不會被匹配。

你可以通過實現IRouteConstraint介面來實現一個自訂路由。這是一個極其簡單的介面,它只描述了一個方法:

bool Match(     HttpContextBase httpContext,     Route route,     string parameterName,     RouteValueDictionary values,     RouteDirection routeDirection )

這個方法返回一個布爾值。如果返回了false,與約束相關聯的路由將不會匹配瀏覽器請求。

Localhost約束包含在了代碼清單1中。

代碼清單1 - LocalhostConstraint.cs

using System.Web;

using System.Web.Routing;
namespace MvcApplication1.Constraints {

    public class LocalhostConstraint : IRouteConstraint

    {

        public bool Match

            (  

               HttpContextBase httpContext,

                Route route,

                string parameterName,

                RouteValueDictionary values,

                RouteDirection routeDirection

            )

        {

            return httpContext.Request.IsLocal;

        }
    }

}

代碼清單1中的約束利用了HttpRequest類公布的IsLocal屬性。當發出請求的IP地址是127.0.0.1或者與伺服器的IP地址相同時,這個屬性返回true。

你在定義於Global.asax的路由中使用了自訂約束。代碼清單2中的Global.asax檔案使用了Localhost約束來阻止任何人請求Admin頁面,除非他們從本機伺服器發出請求。舉個例子,當請求來自遠程伺服器時,對於/Admin/DeleteAll的請求將會失敗。

代碼清單2 - Global.asax

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Web.Routing;

using MvcApplication1.Constraints;
namespace MvcApplication1 {
    public class MvcApplication : System.Web.HttpApplication

    {

        public static void RegisterRoutes(RouteCollection routes){

            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(                 "Admin",                 "Admin/{action}",

                new {controller="Admin"},

                new {isLocal=new LocalhostConstraint()}

            );
            //routes.MapRoute(             //    "Default",                                           // Route name  

           //    "{controller}/{action}/{id}",                           // URL with parameters

            //    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults             //);

        }
        protected void Application_Start(){

            RegisterRoutes(RouteTable.Routes);

        }

    }

}

Localhost約束使用在了Admin路由的定義中。這個路由不會被遠程瀏覽器請求所匹配。然而,應該意識到定義在Global.asax中的其他路由可能會匹配相同的請求。理解這一點很重要:約束阻止了特定路由匹配某一請求,而不是所有定義在Global.asax檔案中的路由。

注意到Default路由在代碼清單2中的Glabal.asax檔案中被注釋掉了。如果你包含Default路由,那麼Default路由將會匹配對Admin控制器的請求。在這種情況下,遠端使用者仍然可以調用Admin控制器的動作,即使他們的請求不匹配Admin路由。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.