zoukankan      html  css  js  c++  java
  • Urls, Routing and Area in Asp.net MVC

          本文着重讲述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

  • 相关阅读:
    波段是金牢记六大诀窍
    zk kafka mariadb scala flink integration
    Oracle 体系结构详解
    图解 Database Buffer Cache 内部原理(二)
    SQL Server 字符集介绍及修改方法演示
    SQL Server 2012 备份与还原详解
    SQL Server 2012 查询数据库中所有表的名称和行数
    SQL Server 2012 查询数据库中表格主键信息
    SQL Server 2012 查询数据库中所有表的索引信息
    图解 Database Buffer Cache 内部原理(一)
  • 原文地址:https://www.cnblogs.com/Langzi127/p/2732725.html
Copyright © 2011-2022 走看看