【轉】Asp.Net MVC4 之Url路由

MVC4常見路由的處理方式html

//直接方法重載+匿名對象
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

            //構造路由而後添加
            Route myroute = new Route("{controller}/{action}", new MvcRouteHandler());
            routes.Add("MyRoute0", myroute);

            //跨命名空間路由
            routes.MapRoute(
                "AddContollerRoute",
                "Home/{action}/{id}/{*catchall}",
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                new[] { "URLsAndRoutes.AdditionalControllers" }
            );
            routes.MapRoute(
                "MyRoute1",
                "{controller}/{action}/{id}/{*catchall}",
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                new[] { "URLsAndRoutes.Controllers" }
            );
            
            //可變長度路由 + 正則表達式匹配路由
            routes.MapRoute(
                "MyRoute2",
                "{controller}/{action}/{id}/{*catchall}",
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                new { controller = "^H.*", action = "^Index$|^About$" },
                new[] { "URLsAndRoutes.Controllers" }
            );

            //指定請求方法
            routes.MapRoute("MyRoute3", "{controller}/{action}/{id}/{*catchall}",
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                new { controller = "^H.*", action = "Index|About", httpMethod = new HttpMethodConstraint("GET") },
                new[] { "URLsAndRoutes.Controllers" }
            );

 

 

 

先來看下面兩個個url,對比一下:web

  1. http://xxx.yyy.com/Admin/UserManager.aspx
  2. http://xxx.yyy.com/Admin/DeleteUser/1001 

   對於第1個Url,假設它與服務器上的文件有直接的關係,那麼服務器在接受客戶端請求並將對應的文件傳送給客戶端。咱們大概能夠猜到它是對用戶管理的一個頁面,它的物理文件UserManager.aspx在網站根目錄下面的Admin文件夾中。而第2個url,在不知道Mvc路由以及Url重寫時,很難猜到這個Url背後具體有些什麼,前提條件是基於.Net框架開發的Web項目。 那麼在這裏,咱們引入Asp.Net Mvc Url路由這個概念,也正是本文所要闡述的主題,Url路由模塊是負責映射從瀏覽器請求到特定的控制器動做。正則表達式

  基於上面提到的Url路由以及其做用,咱們就大概能猜到第2個Url背後有些啥了。 天然而然的Admin就是控制器了,DeleteUser是控制器裏面的動做及Action了,1001就是Action的參數。到這裏咱們對Url路由有一個簡單的認識,那麼接着看下面一組url,假設Home是控制器,Index是控制器裏面的Action,至於1001或者2345這類數據咱們暫且約定爲參數:瀏覽器

  1. http://xxx.yyy.com
  2. http://xxx.yyy.com/Home/1001
  3. http://xxx.yyy.com/Index/1001
  4. http://xxx.yyy.com/Home/Index/1001/2345
  5. http://xxx.yyy.com/System/Home/Index/1001

按照約定,從上面的幾組Url中能夠看出,有的缺控制器,有的缺Action,有的帶有好幾個參數,有的又莫名的多出了控制器、Action、參數以外的東西,那麼他們能正確的訪問嗎?服務器

  註冊路由mvc

    在vs2012裏面新建一個asp.net mvc4 web 應用程序項目,能夠在項目的根目錄下App_Start裏面看到RouteConfig文件,在這個文件裏面就能夠添加url路由了。在文件裏面能夠看到MapRoute 這個方法,它是RouteCollection的擴展方法,而且含有多個重載,下面看看MapRoute方法的參數,這裏選參數最多的那個:框架

public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces);

從上面的代碼片斷大概能夠看出該方法含有路由名稱、路由的Url、默認值、約束、首先查找路由所在的命名空間,該方法是返回對映射路由的引用。不過在RouteConfig文件中咱們能夠看到添加路由的代碼:asp.net

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

稍微分析一下這段代碼能夠知道,在路由集合裏面添加了一個名爲"Default"的路由,而且有一個默認的路由指向,指向名稱爲Home的控制器,而且Action爲Index,能夠明顯的看到這裏 "id = UrlParameter.Optional" 的寫法,它的意思就是說Action的參數能夠不須要用戶來指定,能夠缺省。函數

  多個參數如何傳遞網站

  以前在一個QQ羣裏面見到一兄弟在問,相似這樣的Url「http://xxx.yyy.com/Home/Index/1001/2345/tauruswu」在路由裏面怎麼配置?其實這個也很簡單,咱們只須要將路由配置、以及Action稍做調整。

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

這個是調整以後的路由配置,只須要在Url後面再加上"{*catchall}"便可,那麼Action調整以後以下

public ActionResult Index(string id,string catchall)
{
  ViewBag.Message = "修改此模板以快速啓動你的 ASP.NET MVC 應用程序。";

  return View();
}

在Action裏面定義了一個參數"catchall"用來接收Url中除了Id以外其餘全部的參數值,那麼上面那個Url中接收的參數就是「2345/tauruswu」,這樣看起來好很差了,不過我以爲怪怪的,有沒有更好的解決方法了?Url有必要寫成那麼長嗎?這個問題在後續文章中會涉及到。

  你也許會犯的錯誤

  不知各位兄弟在剛剛接觸MVC的時候,有沒有碰到過這樣的問題,以下圖

那麼這個錯誤是如何引發的了?代碼是這麼寫的

複製代碼
namespace MvcDebug.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index(string id,string catchall)
        {
            ViewBag.Message = "修改此模板以快速啓動你的 ASP.NET MVC 應用程序。";

            return View();
        }
    }
}

namespace MvcDebug.Controllers.Tauruswu
{
    public class HomeController : Controller
    {
        public ActionResult Index(string id, string catchall)
        {
            ViewBag.Message = "修改此模板以快速啓動你的 ASP.NET MVC 應用程序。";

            return View();
        }
    }
}
複製代碼

咱們再看英文提示大概就是說匹配出了多個控制器爲"Home"的類型,在它的提示中也告訴了咱們解決方法,說在MapRoute方法中使用"namespaces"這個參數,咱們先將這個放在一邊,將拋出這個錯誤的源碼給找出來,具體的源碼在DefaultControllerFactory這個類中,看類名就能猜出它的做用是什麼了。

複製代碼
private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces)
{
  // Once the master list of controllers has been created we can quickly index into it
  ControllerTypeCache.EnsureInitialized(BuildManager);

  ICollection<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
  switch (matchingTypes.Count)   {     case 0:   // no matching types   return null;    case 1:   // single matching type   return matchingTypes.First();   default:   // multiple matching types   throw CreateAmbiguousControllerException(route, controllerName, matchingTypes);   } }
複製代碼

上面粗體標出的代碼,由於咱們在路由中沒有配置「namespaces」這個參數,這段代碼的意思就是經過控制器名稱獲取所匹配的控制器類型集合,當獲取到集合數據以後就開始Case了,很明顯,這裏集合的數目是2,天然就拋錯了。

  問題出來了,該如何解決?在錯誤提示中說要用到「namespaces」這個參數,咱們能不能告訴MVC解析引擎,在解析控制器名稱時,能不能對某些命名空間進行優先處理,事實上是能夠的,只須要在配置路由的地方稍微調整一下

routes.MapRoute(
  name: "Default",
  url: "{controller}/{action}/{id}/{*catchall}",
  defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
  namespaces: new[] { "MvcDebug.Controllers.Tauruswu" }
);

上面標粗的代碼的意思是說優先解析「MvcDebug.Controllers.Tauruswu」這個命名空間裏面的控制器。

  路由約束

  對於路由約束,我我的以爲這個可能在實際開發中用的不是不少,既然MapRoute方法提供了constraints這個參數及約束,那麼在某些特定的場合確定能發揮它的做用。在MVC中提供了三種路由約束方案,分別是: 1)正則表達式 ,2)http方法 ,3)自定義約束 。下面咱們分別介紹下這三種約束的使用方法。

  1)正則表達式 ,在路由配置中,咱們作了這樣的規則,只匹配Controller名稱以H開頭的

複製代碼
routes.MapRoute(
  name: "Default",
  url: "{controller}/{action}/{id}/{*catchall}",
  defaults: new { controller = "Demo", action = "Index", id = UrlParameter.Optional },
  constraints: new { controller = "^H.*" },
  namespaces: new[] { "MvcDebug.Controllers.Tauruswu" }
);
複製代碼

  2) http方法 ,咱們將路由配置稍做修改

複製代碼
routes.MapRoute(
  name: "Default",
  url: "{controller}/{action}/{id}/{*catchall}",
  defaults: new { controller = "Demo", action = "Index", id = UrlParameter.Optional },
  constraints: new { httpMethod = new HttpMethodConstraint("POST"), },
  namespaces: new[] { "MvcDebug.Controllers.Tauruswu" }
);
複製代碼

而後在對應的Action上面打個標記

複製代碼
[HttpGet]
public ActionResult Index()
{
  ViewBag.Message = "修改此模板以快速啓動你的 ASP.NET MVC 應用程序。";

  return View();
}
複製代碼

大家說這樣行不行了?

   3) 自定義約束,若是說上面兩種需求仍是不能知足你,那麼咱們能夠自定義約束。咱們翻看HttpMethodConstraint這個類,能夠看到它是繼承IRouteConstraint這個接口,其定義是

public interface IRouteConstraint
{
  bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
}

裏面只有一個布爾類型的方法,關於這個例子,是從網上借鑑過來的,以下

複製代碼
public class CustomConstraint : IRouteConstraint
{
  private string requiredAgent;

  public CustomConstraint(string agentArgs)
  {
    this.requiredAgent = agentArgs;
  }

  public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
  {
    return httpContext.Request.UserAgent != null && httpContext.Request.UserAgent.Contains(requiredAgent);
  }

}
複製代碼

這段代碼的意思就是檢查客戶端請求的UserAgent屬性值,看它是否含有一個被傳遞給構造函數的值。那麼咱們將路由做以下修改

複製代碼
routes.MapRoute(
  name: "Default",
  url: "{controller}/{action}/{id}/{*catchall}",
  defaults: new { controller = "Demo", action = "Index", id = UrlParameter.Optional },
  constraints: new { customConstraint = new CustomConstraint("IE"), },
  namespaces: new[] { "MvcDebug.Controllers.Tauruswu" }
);
複製代碼

很顯然,這個只能在IE遊覽器下面遊覽。

  如何建立自定義路由處理程序

  在翻閱MapRoute方法源碼的時候,看到了這麼一段

複製代碼
Route route = new Route(url, new MvcRouteHandler())
{
  Defaults = CreateRouteValueDictionary(defaults),
  Constraints = CreateRouteValueDictionary(constraints),
  DataTokens = new RouteValueDictionary()
};

.....
routes.Add(name, route);
複製代碼

在Route實例化的時候,它是用到了「MvcRouteHandler「這個類,該類繼承」IRouteHandler「接口,若是咱們不用系統裏面已經定義好的路由處理方案,咱們要本身來實現一套?改怎麼下手,此時只須要繼承」IRouteHandler「這個接口並實現」GetHttpHandler「方法便可。

   最後在添加路由時,像這樣操做

routes.Add(new Route("DemoUrl",new DemoRouteHandler());

當咱們在遊覽器裏面請求/DemoUrl這個地址時,就會用到咱們自定義的處理程序,在實際開發當中,若是真的要用到自定義路由處理程序,那麼咱們就要實現不少本來框架所實現的空能,雖然這給咱們帶來了很大的擴展空間,可是又不可控。

  總結

  Url路由系統是經過請求地址進行解析從而獲得以目標Controller/Action名稱爲核心的路由數據,Url路由系統是創建在Asp.net 之上,咱們在調試System.Web.Routing的源碼時候能夠得知。在這裏咱們由淺入深的瞭解了路由系統,接下來咱們會講到控制器以及Action,也是最爲核心的東西。

 

轉發處:http://www.cnblogs.com/wucj/p/3113878.html

相關文章
相關標籤/搜索