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

  • 相关阅读:
    计算机基础 python基础内容 变量以及基础数据类型
    初识函数--生成器
    初识函数--迭代器
    初识函数
    初识小数据池和深浅拷贝
    初识基础数据类型 dict,set
    初识基础数据类型 list,tuple
    初识基础数据类型 int,str,bool
    初识变量
    自定义协议解决粘包问题,阿里云部署,udp协议套接字,socketserver模块的使用
  • 原文地址:https://www.cnblogs.com/Langzi127/p/2732725.html
Copyright © 2011-2022 走看看