为了账号安全,请及时绑定邮箱和手机立即绑定

为什么要在ASP.NETMVC中的公共路由之前先映射特殊路由?

为什么要在ASP.NETMVC中的公共路由之前先映射特殊路由?

天涯尽头无女友 2019-06-21 13:25:02
为什么要在ASP.NETMVC中的公共路由之前先映射特殊路由?来自万维网:.路由引擎将选择与所提供的URL匹配的第一条路由,并尝试在该路由中使用路由值。因此,应该先将不太常见或更专门化的路由添加到表中,而更一般的路由则应在稍后添加.为什么我要先绘制专用线呢?有人可以给我举个例子,在哪里我可以看到“地图公共路线第一”的失败?
查看完整描述

1 回答

?
慕莱坞森

TA贡献1810条经验 获得超4个赞

路由引擎将选择与提供的URL匹配的第一条路由,并尝试在该路由中使用路由值。

发生这种情况的原因是RouteTable被用作开关箱语句。图如下:

int caseSwitch = 1;switch (caseSwitch){
    case 1:
        Console.WriteLine("Case 1");
        break;
    case 1:
        Console.WriteLine("Second Case 1");
        break;
    default:
        Console.WriteLine("Default case");
        break;}

如果caseSwitch1,第二个块永远不会到达,因为第一个块捕获了它。

Route类遵循类似的模式(在GetRouteDataGetVirtualPath方法)。他们可以返回两个州:

  1. 一组路由值(或

    VirtualPath

    对象的

    GetVirtualPath

    )。这表示与请求匹配的路由。
  2. null

    ..这表明路由与请求不匹配。

在第一种情况下,mvc使用路由生成的路由值来查找Action方法。在这种情况下,RouteTable没有进一步分析。

在第二个例子中,mvc将检查下一个RouteRouteTable要查看它是否与请求匹配(内置行为与URL和约束匹配,但技术上可以匹配HTTP请求中的任何内容)。再一次,该路线可以返回一组RouteValuesnull取决于结果。

如果您尝试像上面那样使用一个Switch-case语句,程序将不会编译.但是,如果您配置的路由绝不可能回报null或返回RouteValues对象在更多情况下,程序将编译,但会出现错误行为。

错误配置示例

下面是我经常在StackOverflow(或它的某些变体)上看到的典型例子:

public class RouteConfig{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

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

在本例中:

  1. CustomRoute

    将匹配任何长度为1、2或3段的URL(注意,

    segment1

    是必需的,因为它没有默认值)。
  2. Default

    将匹配任何长度为0、1、2或3段的URL。

因此,如果应用程序被传递,则URL\Home\AboutCustomRoute将匹配,并提供以下内容RouteValues转到MVC:

  1. segment1 = "Home"

  2. controller = "MyController"

  3. action = "About"

  4. id = {}

这将使mvc寻找一个名为About在一个名为MyControllerController如果它不存在就会失败。这个Default在这种情况下,路由是一个无法到达的执行路径,因为即使它将匹配一个2段URL,框架也不会给它机会,因为第一次匹配获胜。

修复配置

关于如何继续修复配置,有几个选项。但所有这些都取决于第一场比赛获胜然后路由就不会再看下去了。

选项1:添加一个或多个文字段

public class RouteConfig{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "CustomRoute",
            url: "Custom/{action}/{id}",

            // Note, leaving `action` and `id` out of the defaults
            // makes them required, so the URL will only match if 3
            // segments are supplied begining with Custom or custom.
            // Example: Custom/Details/343
            defaults: new { controller = "MyController" }
        );

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

选项2:添加1个或更多RegEx约束

public class RouteConfig{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "CustomRoute",
            url: "{segment1}/{action}/{id}",
            defaults: new { controller = "MyController", action = "Index", id = UrlParameter.Optional },
            constraints: new { segment1 = @"house|car|bus" }
        );

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

选项3:添加1个或更多自定义约束

public class CorrectDateConstraint : IRouteConstraint{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName,
    RouteValueDictionary values, RouteDirection routeDirection)
    {
        var year = values["year"] as string;
        var month = values["month"] as string;
        var day = values["day"] as string;

        DateTime theDate;
        return DateTime.TryParse(year + "-" + month + "-" + day, System.Globalization.CultureInfo.InvariantCulture,
         DateTimeStyles.None, out theDate);
    }}public class RouteConfig{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "CustomRoute",
            url: "{year}/{month}/{day}/{article}",
            defaults: new { controller = "News", action = "ArticleDetails" },
            constraints: new { year = new CorrectDateConstraint() }
        );

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

备选案文4:使所需段+使段数与现有路由不匹配

public class RouteConfig{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "CustomRoute",
            url: "{segment1}/{segment2}/{action}/{id}",
            defaults: new { controller = "MyController" }
        );

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

在上述情况下,CustomRoute将只匹配一个URL与4个段(注意,这些可以是任何值)。这个Default像以前一样,路由只匹配0、1、2或3段的URL。因此,没有无法到达的执行路径。

选项5:为自定义行为实现RouteBase(或路由)

路由不支持开箱即用的任何东西(例如特定域或子域上的匹配)都可以通过实现你自己的RouteBase子类或者路线子类。这也是理解路由工作方式的最佳方法。

public class SubdomainRoute : Route{
    public SubdomainRoute(string url) : base(url, new MvcRouteHandler()) {}

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var routeData = base.GetRouteData(httpContext);
        if (routeData == null) return null; // Only look at the subdomain if this route matches in the first place.
        string subdomain = httpContext.Request.Params["subdomain"]; 
        // A subdomain specified as a query parameter takes precedence over the hostname.
        if (subdomain == null) {
            string host = httpContext.Request.Headers["Host"];
            int index = host.IndexOf('.');
            if (index >= 0)
                subdomain = host.Substring(0, index);
        }
        if (subdomain != null)
            routeData.Values["subdomain"] = subdomain;
        return routeData;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        object subdomainParam = requestContext.HttpContext.Request.Params["subdomain"];
        if (subdomainParam != null)
            values["subdomain"] = subdomainParam;
        return base.GetVirtualPath(requestContext, values);
    }}

这个班是从下列地方借来的:是否可以基于子域创建ASP.NETMVC路由?

public class RouteConfig{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.Add(new SubdomainRoute(url: "somewhere/unique"));

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

注:这里真正的问题是,大多数人认为他们的路线都应该看起来像Default路线。收到,粘贴,完成,对吧?不对。

这种方法通常会产生两个问题:

  1. 几乎所有其他路由都应该至少有一个文字段(如果您喜欢这类内容,则应该有一个约束)。
  2. 最符合逻辑的行为通常是使其他路线具有

    所需

    片段。

另一个常见的误解是,可选段意味着您可以省略。任何段,但在现实中,您只能离开最右边的一段或几段。

微软成功地实现了基于路由约定的、可扩展的和强大的路由协议.他们没能直观地理解。几乎每个人第一次尝试都失败了(我知道我成功了!)幸运的是,一旦你了解了它的工作原理,就不难了。


查看完整回答
反对 回复 2019-06-21
  • 1 回答
  • 0 关注
  • 353 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信