• MVC 区域路


    VS2013和VS2015中MVC 区域路由匹配顺序相反

     

    创建测试工程

    分别在vs2013和vs2015中创建mvc项目,并创建First、Second、Three三个Area,每个Area下面创建一个HomeController和Index视图。修改RouteConfig.cs中的路由注册方法,添加命名空间

    复制代码
           public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                    namespaces: new[] { "RouteDebuggerMvc5Demo.Controllers" }
                );
            }
    复制代码

    修改三个Area的路由注册方法,添加命名空间:

    复制代码
      public class FirstAreaRegistration : AreaRegistration 
        {
            public override string AreaName 
            {
                get 
                {
                    return "First";
                }
            }
    
            public override void RegisterArea(AreaRegistrationContext context) 
            {
                context.MapRoute(
                    "First_default",
                    "First/{controller}/{action}/{id}",
                    new { action = "Index", id = UrlParameter.Optional },
                    namespaces: new[] { "RouteDebuggerMvc5Demo.Areas.First.Controllers" }
                );
            }
        }
    复制代码

    使用nuget添加RouteDebugger引用,在Web.config中配置启用 <add key="RouteDebugger:Enabled" value="true" />,运行起来:

    VS2013中升序

    Matches Current RequestUrlDefaultsConstraintsDataTokens
    False First/{controller}/{action}/{id} action = Index, id = UrlParameter.Optional (empty) Namespaces = RouteDebuggerMvc5Demo.Areas.First.Controllers, area = First, UseNamespaceFallback = False
    False Second/{controller}/{action}/{id} action = Index, id = UrlParameter.Optional (empty) Namespaces = RouteDebuggerMvc5Demo.Areas.Second.Controllers, area = Second, UseNamespaceFallback = False
    False Three/{controller}/{action}/{id} action = Index, id = UrlParameter.Optional (empty) Namespaces = RouteDebuggerMvc5Demo.Areas.Three.Controllers, area = Three, UseNamespaceFallback = False
    False {resource}.axd/{*pathInfo} (null) (empty) (null)
    True {controller}/{action}/{id} controller = Home, action = Index, id = UrlParameter.Optional (empty) Namespaces = RouteDebuggerMvc5Demo.Controllers
    True {*catchall} (null) (null) (null)

    VS2015中降序

    All Routes
    Matches Current RequestUrlDefaultsConstraintsDataTokens
    False Three/{controller}/{action}/{id} action = Index, id = UrlParameter.Optional (empty) Namespaces = RouteDebuggerDemo.Areas.Three.Controllers, RouteDebuggerDemo, area = Three, UseNamespaceFallback = False
    False Second/{controller}/{action}/{id} action = Index, id = UrlParameter.Optional (empty) Namespaces = RouteDebuggerDemo.Areas.Second.Controllers, RouteDebuggerDemo, area = Second, UseNamespaceFallback = False
    False First/{controller}/{action}/{id} action = Index, id = UrlParameter.Optional (empty) Namespaces = RouteDebuggerDemo.Areas.First.Controllers, RouteDebuggerDemo, area = First, UseNamespaceFallback = False
    False {resource}.axd/{*pathInfo} (null) (empty) (null)
    True {controller}/{action}/{id} controller = Home, action = Index, id = UrlParameter.Optional (empty) Namespaces = RouteDebuggerDemo.Controllers, RouteDebuggerDemo
    True {*catchall} (null) (null) (null)

    如果在VS2013中的某个路由注册有问题,一直没有显现出来,升级到VS2015后出现了404就有可能是路由匹配顺序导致的。问题参考:Why did the order of Areas in RegisterAllAreas change with Visual Studio 2015?

    手动修改路由顺序

    默认在 *.csproj文件中的路由顺序是

    <Compile Include="AreasFirstControllersHomeController.cs" />
    <Compile Include="AreasFirstFirstAreaRegistration.cs" />
    <Compile Include="AreasSecondControllersHomeController.cs" />
    <Compile Include="AreasSecondSecondAreaRegistration.cs" />
    <Compile Include="AreasThreeControllersHomeController.cs" />
    <Compile Include="AreasThreeThreeAreaRegistration.cs" />

    修改成

    <Compile Include="AreasFirstControllersHomeController.cs" />
    <Compile Include="AreasFirstFirstAreaRegistration.cs" /> 
    <Compile Include="AreasThreeControllersHomeController.cs" />
    <Compile Include="AreasThreeThreeAreaRegistration.cs" />
    <Compile Include="AreasSecondControllersHomeController.cs" />
    <Compile Include="AreasSecondSecondAreaRegistration.cs" />

    重新访问页面查看路由,顺序已经改变了(对比上面VS2015里的顺序),可以通过这种方法把最常用的路由调到最前面,提高匹配速度。经测试添加新的Area后,调整的路由顺序不会变回去,可以放心使用。

    参考链接:.Net MVC Area Registration Sequence

    RouteDebugger 简介

    作者地址:ASP.NET Routing Debugger,代码结构图如下:

    程序集属性[assembly: PreApplicationStartMethod(typeof(PreApplicationStart), "Start")] 使PreApplicationStart的Start方法在Application_Start方法之前执行,

    复制代码
    public static void Start()
            {
                bool flag = Convert.ToBoolean(ConfigurationManager.AppSettings["RouteDebugger:Enabled"]);
                if (flag)
                {
                    DynamicModuleUtility.RegisterModule(typeof(RouteDebuggerHttpModule));
                }
            }
    复制代码

    最后处理请求的是DebugHttpHandler里的ProcessRequest方法把匹配的路由信息获取出来,代码如下:

    复制代码
      //处理请求
            public void ProcessRequest(HttpContext context)
            {
                HttpRequest request = context.Request;
                if (!this.IsRoutedRequest(request) || context.Response.ContentType == null || !context.Response.ContentType.Equals("text/html", StringComparison.OrdinalIgnoreCase))
                {
                    return;
                }
                string text = string.Empty;
                RequestContext requestContext = request.RequestContext;
                if (request.QueryString.Count > 0)
                {
                    RouteValueDictionary routeValueDictionary = new RouteValueDictionary();
                    foreach (string text2 in request.QueryString.Keys)
                    {
                        if (text2 != null)
                        {
                            routeValueDictionary.Add(text2, request.QueryString[text2]);
                        }
                    }
                    VirtualPathData virtualPath = RouteTable.Routes.GetVirtualPath(requestContext, routeValueDictionary);
                    if (virtualPath != null)
                    {
                        text = "<p><label style="font-weight: bold; font-size: 1.1em;">Generated URL</label>: ";
                        text = text + "<strong style="color: #00a;">" + virtualPath.VirtualPath + "</strong>";
                        Route route = virtualPath.Route as Route;
                        if (route != null)
                        {
                            text = text + " using the route "" + route.Url + ""</p>";
                        }
                    }
                }
                string text3 = string.Empty;
                RouteData routeData = requestContext.RouteData;
                RouteValueDictionary values = routeData.Values;
                RouteBase route2 = routeData.Route;
                string text4 = string.Empty;
                using (RouteTable.Routes.GetReadLock())
                {
                    foreach (RouteBase current in RouteTable.Routes)
                    {
                        //查询请求与路由是否匹配
                        bool flag = current.GetRouteData(requestContext.HttpContext) != null;
                        
                        string isMatchRequest = string.Format("<span{0}>{1}</span>", DebugHttpHandler.BoolStyle(flag), flag);
                        string url = "n/a";
                        string defaults = "n/a";
                        string constraints = "n/a";
                        string dataTokens = "n/a";
                        Route route3 = this.CastRoute(current);
                        if (route3 != null)
                        {
                            url = route3.Url;
                            defaults = DebugHttpHandler.FormatDictionary(route3.Defaults);
                            constraints = DebugHttpHandler.FormatDictionary(route3.Constraints);
                            dataTokens = DebugHttpHandler.FormatDictionary(route3.DataTokens);
                        }
                 
                     text4 += string.Format("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>", new object[]
                        {
                            isMatchRequest,
                            url,
                            defaults,
                            constraints,
                            dataTokens
                        });
                    }
                }
                string text10 = "n/a";
                string text11 = "";
                if (!(route2 is DebugRoute))
                {
                    foreach (string current2 in values.Keys)
                    {
                        text3 += string.Format("	<tr><td>{0}</td><td>{1}&nbsp;</td></tr>", current2, values[current2]);
                    }
                    foreach (string current3 in routeData.DataTokens.Keys)
                    {
                        text11 += string.Format("	<tr><td>{0}</td><td>{1}&nbsp;</td></tr>", current3, routeData.DataTokens[current3]);
                    }
                    Route route4 = route2 as Route;
                    if (route4 != null)
                    {
                        text10 = route4.Url;
                    }
                }
                else
                {
                    text10 = string.Format("<strong{0}>NO MATCH!</strong>", DebugHttpHandler.BoolStyle(false));
                }
                text3 = "text3";
                text10 = "text10";
                text4 = "text4";
                text11 = "text11";
                text = "text";
    
    
                context.Response.Write(string.Format("<html>
    <div id="haackroutedebugger" style=" padding-bottom: 10px;">
        <style>
            #haackroutedebugger, #haackroutedebugger td, #haackroutedebugger th {{background-color: #fff; font-family: verdana, helvetica, san-serif; font-size: small;}}
            #haackroutedebugger tr.header td, #haackroutedebugger tr.header th {{background-color: #ffc;}}
        </style>
        <hr style=" 100%; border: solid 1px #000; margin:0; padding:0;" />
        <h1 style="margin: 0; padding: 4px; border-bottom: solid 1px #bbb; padding-left: 10px; font-size: 1.2em; background-color: #ffc;">Route Debugger</h1>
        <div id="main" style="margin-top:0; padding: 0 10px;">
            <p style="font-size: .9em; padding-top:0">
                Type in a url in the address bar to see which defined routes match it. 
                A {{*catchall}} route is added to the list of routes automatically in 
                case none of your routes match.
            </p>
            <p style="font-size: .9em;">
                To generate URLs using routing, supply route values via the query string. example: <code>http://localhost:14230/?id=123</code>
            </p>
            <p><label style="font-weight: bold; font-size: 1.1em;">Matched Route</label>: {1}</p>
            {5}
            <div style="float: left;">
                <table border="1" cellpadding="3" cellspacing="0" width="300">
                    <caption style="font-weight: bold;">Route Data</caption>
                    <tr class="header"><th>Key</th><th>Value</th></tr>
                    {0}
                </table>
            </div>
            <div style="float: left; margin-left: 10px;">
                <table border="1" cellpadding="3" cellspacing="0" width="300">
                    <caption style="font-weight: bold;">Data Tokens</caption>
                    <tr class="header"><th>Key</th><th>Value</th></tr>
                    {4}
                </table>
            </div>
            <hr style="clear: both;" />
            <table border="1" cellpadding="3" cellspacing="0">
                <caption style="font-weight: bold;">All Routes</caption>
                <tr class="header">
                    <th>Matches Current Request</th>
                    <th>Url</th>
                    <th>Defaults</th>
                    <th>Constraints</th>
                    <th>DataTokens</th>
                </tr>
                {2}
            </table>
            <hr />
            <h3>Current Request Info</h3>
            <p>
                AppRelativeCurrentExecutionFilePath is the portion of the request that Routing acts on.
            </p>
            <p><strong>AppRelativeCurrentExecutionFilePath</strong>: {3}</p>
        </div>
    </div>", new object[]
                {
                    text3,
                    text10,
                    text4,
                    request.AppRelativeCurrentExecutionFilePath,
                    text11,
                    text
                }));
            }
    复制代码

    注:RouteDebugger的源码是通过ILSpy反编译的。

    结束语

     抽出时间去验证路由顺序是因为两个错误引起的:

    1.升级vs2013的mvc4解决方案到vs2015后所有的Area路由失效,返回404错误。以为是vs2015对mvc4的支持不够(刚出来是对mvc4是有点问题),就撤销升级返回vs2013了。

    2.在vs2013中添加了一个新的Area,排在了以前的所有路由之后,出现404错误。通过RouteDebugger发现是路由的顺序问题,所以想到上面那个问题应该也是顺序问题,不应该是vs2015的缺陷。

  • 相关阅读:
    JavaScript条件判断和循环
    JavaScript数据类型详解
    Dockerfile使用
    让ie8、ie9支持媒体查询
    事件穿透
    判断是苹果手机还是安卓手机
    ES6中字符串的扩展
    ES6数组的扩展运算符
    let和const
    ES6中函数的扩展
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/6622521.html
Copyright © 2020-2023  润新知