Urls, Routing and Area in Asp.net MVC

本文深入探讨ASP.NET MVC中的路由配置,包括基本配置、URL片段处理、自定义路由及Area使用。涵盖默认值设定、静态片段应用、可选片段、自定义约束、物理文件路由等高级主题。

      本文着重讲述Asp.net MVC的路由配置,url灵活动态输出以及Area的使用。

 

      一、路由配置:这里忽略VS2010给默认生成的Route,按照先易后难的顺序来讲解。

      在讲解route之前,先说一下url segment的概念。如对于url:http://mydomain.com/admin/index 它的segment有2个。第一个是admin,第二个是index。基于segment,关于Route匹配规则,有3点特征:1)保守,它只匹配包含相同个数segment的url(Route配置中有默认值或者是optional的例外);2)开明,只要segment个数相同,它就接纳(有特殊constraint配置除外);3)知足,只要一个url匹配了前边的Route,后面的就不在去匹配。下面是示例: 

      1. 最简单的做法是:

      Route myRoute =  new Route( " {controller}/{action} "new MvcRouteHandler());
      routes.Add( " myRoute ", myRoute);

 当然,相同作用但更常用的写法是:

      routes.MapRoute( " myRoute "" {controller}/{action} ");

这里myRoute参数是optional。 下边讲解一些更实际的应用。

      2. 定义默认值:

      routes.MapRoute( " defaultValueRoute "" {controller}/{action} "new { controller =  " Home ", action =  " Index " });

对于上述route配置,http://mydomain.com、http://mydomain.com/customer、http://mydomain.com/customer/list均能匹配上,当controller和action对应的segment部分没有指定时,采用默认值(Home、action),否则采用匹配的segment值。但 http://mydomain.com/customer/list/all 不行,因为它的url segment多于route配置的2个。

      3. 包含静态url片段:

      routes.MapRoute( " staticFolderRoute "" Public/{controller}/{action} ");

它要求url有3个segment,并且第一部分必须是public。如:http://mydomain.com/public/customer/list可以匹配,但http://mydomain.com/customer/list/all不行。同时,请注意:public将不会保存在RouteData中。

      除了将静态片段前置外,还可以将它混合在route配置中:

      routes.MapRoute( " mixedSegmentRoute "" X{controller}/{action} ");

可以看到,X和controller混在一起,它所匹配的url的第一片段必须以X开头,如:

http://mydomain.com/ xcustomer/list。在RouteData中controller的值当然也就不包含X了。

 

      虽然controller和action是2个内置的route key,并且一般情况下它们并存于route配置中。但是它们并不是必须都存在于route的url配置规则中,如下:

      routes.MapRoute( " fakeControllerRoute "" Shop/{action} "new { controller =  " Home " });

将controller直接置死为Home。 而根据示例的配置,发散一下可做它用。试想一下:如果站点的某个action(假设为OldAction)不再被使用了,而它所对应的url已被搜索引擎收录或者不想让它404,这个时候除了可以让action跳转走,还可以通过设置route:

      routes.MapRoute( " NolongerUsedActionRoute "" Shop/OldAction "new { controller =  " Home ", action =  " Index " });

后,再次访问它时,就直接映射到了Home/Index了,但是url然为Shop/OldAction。

      4. 定义custom片段:

      routes.MapRoute( " CustomerVariableRoute "" {controller}/{action}/{id} "new { controller =  " Home ", action =  " Index ", id =  " default id " });

所谓的custom片段,就是除了controller和action的其他的那些,如上文的id。

      5. optional片段:

      routes.MapRoute( " OptionalSegmentRoute "" {controller}/{action}/{id} "new { controller =  " Home ", action =  " Index ", id =  UrlParameter.Optional });

特别之处在于http://mydomain.com/public/customer虽然只包含2个segment,但是它可以匹配上述route的。同时,optional segment和default value segment区别的一点在于,同一url(如http://mydomain.com/public/customer)对于前者,RouteData["id"]是不存在的即RouteData中压根就没有id这一个key,后对于后者它是有的,只不过value为默认值。

     6. segment数可变的route:

      routes.MapRoute( " CustomVariableLengthRoute "" {controller}/{action}/{id}/{ *catchall} "new { controller =  " Home ", action =  " Index ", id = UrlParameter.Optional });

该segment配置以*开头。http://mydomain.com/Customer, http://mydomain.com/Customer/List/All/Delete/Perm均能匹配上它,前者RouteData对应值为null,后者为Delete/Perm。

      7. 在route中使用namespace:

      Route系统默认情况下,只核对controller和action的名称,而对它们所在的namespace则视而不见。当项目中有相同的controller和action名称(如应用到Area时),此时若一个url刚好经过某个route匹配上它,则会报错,因为它不知道采用哪一对。这时就可以去设置核对的优先级了:

      routes.MapRoute( " PrioritizeNamespaceRoute "" {controller}/{action}/{id}/{*catchall} ",
           new { controller =  " Home ", action =  " Index ", id = UrlParameter.Optional },
           new[] {  " URLsAndRoutes.Controllers " });

它会优先去从namespace:URLsAndRoutes.Controllers下去搜寻指定的controller和action,如果没有匹配上再去从别的namespace。

       注意,MapRoute方法最后那个参数虽然是数组,但实质上如果你设置了多个namespace,而敲好又有多个有相同名称的controller和action,那么仍然会报相同的异常,因为数组参数中的namespace它们之间又完全是平等的,没有优先级。解决方法是设置多个route,如下:

      routes.MapRoute( " PrioritizeNamespaceRoute "" {controller}/{action}/{id}/{*catchall} ",
                 new { controller =  " Home ", action =  " Index ", id = UrlParameter.Optional },
                 new[] {  " URLsAndRoutes.Controllers " });
      routes.MapRoute( " PrioritizeNamespaceRoute "" {controller}/{action}/{id}/{*catchall} ",
                 new { controller =  " Home ", action =  " Index ", id = UrlParameter.Optional },
                 new[] {  " AdditionalControllers " });

它们会优先去匹配URLsAndRoutes.Controllers,然后去匹配AdditionalControllers,再去匹配其他的。

      如果要限定controller所在的namespace,怎么办?

      Route myRoute = routes.MapRoute( " AddContollerRoute "" Home/{action}/{id}/{*catchall} ",
                 new { controller =  " Home ", action =  " Index ", id = UrlParameter.Optional },
                 new[] {  " AdditionalControllers " });
      myRoute.DataTokens[ " UseNamespaceFallback "] =  false;
      routes.Add(myRoute);

myRoute会仅在AdditionalControllers中查找所指定的controller和action,如果没有直接退出route系统并报错。

      8. 条件限定Route:

      通过正则表达式,可以对route中的各个segment所对应的值进行限定:

      routes.MapRoute( " ConstraintRoute "" {controller}/{action}/{id}/{*catchall} ",
                 new { controller =  " Home ", action =  " Index ", id = UrlParameter.Optional },
                 new { controller =  " ^H.* ", action =  " ^(Index|About)*$ ", httpMethod =  new HttpMethodConstraint( " GET ") }
            );

它限定controller必须以H开头,action只能是index或者about,而http method只能是get方式(这里httpMethod名称可以是其它的,MVC Framework只根据它的值类型HttpMethodConstraint来判断和取值)。

      除了上述限定方法外,你还可以通过实现IRouteConstraint接口,自定义限定方式。如下:

     public  class UserAgentConstraint : IRouteConstraint
    {
         private  string requiredUserAgent;
         public UserAgentConstraint( string agentParam)
        {
            requiredUserAgent = agentParam;
        }

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

然后,使用如下:

     routes.MapRoute( " CustomConstraintRoute "" {controller}/{action}/{id}/{*catchall} ",
          new { controller =  " Home ", action =  " Index ", id = UrlParameter.Optional },
          new { controller =  " ^H.* ", action =  " Index|About ", httpMethod =  new HttpMethodConstraint( " GET "" POST "),  customConstraint =  new  UserAgentConstraint( " IE " ) },
          new[] {  " URLsAndRoutes.Controllers " }
     );

可以看到,只有IE浏览器才能去访问这个route所对应的url了,当然这仅仅是一个demo而已,实际应用并不合适。

      9. 用于物理文件的route:

      MVC Framework的RouteTable.Routes对象有一个RouteExistingFiles属性,默认情况下它为false,即不对物理文件使用route系统。如果要使用,可以设置它为true,然后指定route。如下:

    routes.RouteExistingFiles =  true;
    routes.MapRoute( " DiskFile "" Content/StaticContent.html ",
         new { controller =  " Account ", action =  " LogOn " },
         new { customConstraint =  new UserAgentConstraint( " IE ") });

当访问http://mydomain.com/content/staticcontent.html时,它实际显示的是http://mydomain.com/account/logon对应的内容。

     注意,不要轻易改变RouteExistingFiles的值,如果改变,务必保持其他不走route的静态内容可以访问到,如设置:

    routes.IgnoreRoute( " Content/{filename}.html ");


     二、自定义route系统:

     1. 通过继承RouteBase,创建新Route:

     public  class LegacyRoute : RouteBase
    {
         private  string[] urls;

         public LegacyRoute( params  string[] targetUrls)
        {
            urls = targetUrls;
        }

         public  override RouteData GetRouteData(HttpContextBase httpContext)
        {
            RouteData result =  null;
             string requestedURL =
            httpContext.Request.AppRelativeCurrentExecutionFilePath;
             if (urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase))
            {
                result =  new RouteData( thisnew MvcRouteHandler());
                result.Values.Add( " controller "" Legacy ");
                result.Values.Add( " action "" GetLegacyURL ");
                result.Values.Add( " legacyURL ", requestedURL);
            }
             return result;
        }

         public  override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
             // return null;

            VirtualPathData result =  null;
             if (values.ContainsKey( " legacyURL ") && urls.Contains(( string)values[ " legacyURL "], StringComparer.OrdinalIgnoreCase))
            {
                result =  new VirtualPathData( thisnew UrlHelper(requestContext).Content(( string)values[ " legacyURL "]).Substring( 1));
            }
             return result;
        }
    }

可以看到在GetData方法中,暗度陈仓地指定了controller和action的值,另外还存入了一个legacyURL。

     2. 实现controller和action:

     public  class LegacyController : Controller
    {
         public ActionResult GetLegacyURL( string legacyURL)
        {
             return View(( object)legacyURL);
        }
    }

     3. 配置Route:

    routes.Add( new LegacyRoute( " ~/articles/Windows_3.1_Overview.html "));

这样就简单完成了一个自定义Route系统。效果就是:当浏览http://mydomain.com/articles/Windows_3.1_Overview.html 时会调用刚创建的controller和action,用相同目录其他html文件来访问时,报错404.

     4. 除了上述方案,还可以实现自己的RouteHandler:

     public  class CustomRouteHandler : IRouteHandler
    {
         public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
             return  new CustomHttpHandler();
        }
    }

     public  class CustomHttpHandler : IHttpHandler
    {
         public  bool IsReusable
        {
             get {  return  false; }
        }

         public  void ProcessRequest(HttpContext context)
        {
            context.Response.Write( " Hello ");
        }
    }

应用如下:

    routes.Add( new Route( " SayHello "new  CustomRouteHandler()));

这时访问http://mydomain.com/sayhello,屏幕打印出Hello。

 

      三、使用Areas:

      在vs2010的mvc项目中点右键创建Area,比如为Admin。创建完后,它就又一套自己的结构了,如下:

 

 这个时候,如果有相同的controller和action,就要用到一种描述的在route中指定优先namespace了。另外,需要注意的是view中使用Html.ActionLink方法构造超链接时,需指定area了,如:@Html.ActionLink("Got to Admin Area""Index"new{ area = "Admin"} 。

    源码download

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/Langzi127/archive/2012/10/20/2732725.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值