生成传出 URL 时选择了意外的路由

2024-03-26

请考虑以下路线:

routes.MapRoute(
    "route1",
    "{controller}/{month}-{year}/{action}/{user}"
);
routes.MapRoute(
    "route2",
     "{controller}/{month}-{year}/{action}"
);

以及以下测试:

TEST 1

[TestMethod]
public void Test1()
{
    RouteCollection routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
    RequestContext context = new RequestContext(CreateHttpContext(), 
                                                new RouteData());

    DateTime now = DateTime.Now;
    string result;

    context.RouteData.Values.Add("controller", "Home");
    context.RouteData.Values.Add("action", "Index");
    context.RouteData.Values.Add("user", "user1");
    result = UrlHelper.GenerateUrl(null, "Index", null,
                                    new RouteValueDictionary(
                                        new
                                        {
                                            month = now.Month,
                                            year = now.Year
                                        }),
                                    routes, context, true);
    //OK, result == /Home/10-2012/Index/user1
    Assert.AreEqual(string.Format("/Home/{0}-{1}/Index/user1", now.Month, now.Year), 
                    result);
}

TEST 2

[TestMethod]
public void Test2()
{
    RouteCollection routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
    RequestContext context = new RequestContext(CreateHttpContext(), 
                                                new RouteData());

    DateTime now = DateTime.Now;
    string result;

    context.RouteData.Values.Add("controller", "Home");
    context.RouteData.Values.Add("action", "Index");
    context.RouteData.Values.Add("user", "user1");
    context.RouteData.Values.Add("month", now.Month + 1);
    context.RouteData.Values.Add("year", now.Year);
    result = UrlHelper.GenerateUrl(null, "Index", null,
                                    new RouteValueDictionary(
                                        new
                                        {
                                            month = now.Month,
                                            year = now.Year
                                        }),
                                    routes, context, true);
    //Error because result == /Home/10-2012/Index
    Assert.AreEqual(string.Format("/Home/{0}-{1}/Index/user1", now.Month, now.Year), 
    result);
}

此测试模拟了一种情况,即我在请求上下文中已经有了路由值,并尝试使用 UrlHelper 生成传出 url。

问题是(在测试 2 中呈现),如果我有预期路线中所有路段的值(此处route1)并尝试通过以下方式替换其中一些routeValues参数,所需的路线被省略,并使用下一个合适的路线。

因此测试 1 效果很好,因为请求上下文已经具有路由 1 的 5 个段中的 3 个的值,以及缺少的两个段的值(即,year and month)通过routeValues范围。

测试 2 具有请求上下文中所有 5 个段的值。我想用其他值替换其中一些(即月份和年份)routeValues。但路线1似乎是不合适并使用路线2。

为什么?我的路线有什么问题?

在这种情况下我是否需要手动清除请求上下文?

EDIT

[TestMethod]
public void Test3()
{
    RouteCollection routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
    RequestContext context = new RequestContext(CreateHttpContext(), 
                                                new RouteData());

    DateTime now = DateTime.Now;
    string result;

    context.RouteData.Values.Add("controller", "Home");
    context.RouteData.Values.Add("action", "Index");
    context.RouteData.Values.Add("month", now.Month.ToString());
    context.RouteData.Values.Add("year", now.Year.ToString());
    result = UrlHelper.GenerateUrl(null, "Index", null,
                                    new RouteValueDictionary(
                                        new
                                        {
                                            month = now.Month + 1,
                                            year = now.Year + 1
                                        }),
                                    routes, context, true);
    Assert.AreEqual(string.Format("/Home/{0}-{1}/Index", now.Month + 1, now.Year + 1), 
                    result);
}

这个测试让事情变得更加混乱。这里我正在测试路线2。它有效!我在请求上下文中拥有所有 4 个段的值,通过传递其他值routeValues,生成的外发url就OK了。

所以,问题出在route1上。我缺少什么?

EDIT

From Sanderson S. Freeman A. - 专业 ASP.NET MVC 3 框架第三版:

路由系统按照路由的顺序处理路由 添加到传递给 RegisterRoutes 的 RouteCollection 对象 方法。检查每条路线是否匹配, 需要满足三个条件:

  1. URL 模式中定义的每个段变量都必须有一个可用的值。为了找到每个段变量的值,路由 系统首先查看我们提供的值(使用 匿名类型的属性),那么变量值 当前请求,最后是在中定义的默认值 路线。
  2. 我们为段变量提供的任何值都不能与路由中定义的仅默认变量不一致。我 这些路由没有默认值
  3. 所有段变量的值必须满足路线约束。我对这些路线没有限制

因此,根据我在匿名类型中指定的第一条规则,我没有默认值。当前请求的变量值- 我认为这是来自请求上下文的值。

这些推理对于路线 2 来说有什么问题,而对于路线 1 来说却很有效?

EDIT

实际上,一切都不是从单元测试开始的,而是从真正的 mvc 应用程序开始的。我在那里用过UrlHelper.Action 方法(字符串、对象) http://msdn.microsoft.com/en-us/library/dd460294%28v=vs.108%29.aspx生成传出网址。由于此方法在布局视图(大多数视图的父视图)中使用,因此我已将其纳入我的扩展辅助方法(以排除视图中的额外逻辑),此扩展方法从请求中提取操作名称上下文,作为参数传递给它。我知道我可以通过请求上下文提取所有当前路由值并替换它们year and month(或者我可以创建一个匿名路由值集合,包含上下文中的所有值),但我认为这是多余的,因为 mvc 自动考虑请求上下文中包含的值。因此,我只提取了操作名称,因为没有操作名称就没有 UrlHelper.Action 重载(或者我什至希望“不指定”操作名称),并通过匿名路由值对象添加新的月份和年份。

这是一个扩展方法:

public static MvcHtmlString GetPeriodLink(this HtmlHelper html, 
                                          RequestContext context, 
                                          DateTime date)
{
    UrlHelper urlHelper = new UrlHelper(context);
    return MvcHtmlString.Create(
              urlHelper.Action(
                 (string)context.RouteData.Values["action"], 
                 new { year = date.Year, month = date.Month }));
}

正如我在上面的测试中所描述的,它适用于较短的路线(当请求上下文仅包含控制器、年份和月份以及操作时),但对于较长的路线则失败(当请求上下文包含控制器、年份和月份、操作和用户时) )。


我发布了一个解决方法,用于使路由按我需要的方式工作。

虽然我确实很想知道,为什么我必须在这种情况下采取任何解决方法,以及这两条路线之间的主要区别是什么?route1从工作方式route2 does.


EDIT

另一句话。只要请求上下文中的值是类型string,我决定尝试将它们设置为字符串作为上下文,以确保不存在类型混淆(int 与 string)。我不明白这方面发生了什么变化,但一些路线开始正确生成。但并非全部……这还没有什么意义。我在实际应用程序中更改了此设置,而不是测试,因为测试已int在上下文中,而不是字符串。

嗯,我已经找到了条件route1使用 - 仅当以下值时才使用month and year上下文中的值等于匿名对象中给出的值。如果它们不同(在测试中这对于int and string), route2用来。但为什么?

这证实了我在实际应用中的情况:

  1. I had strings 在上下文中,但提供int通过匿名对象,它以某种方式混淆了 mvc 并且无法使用route1.
  2. 我变了int匿名对象中的 sstrings,以及 urlmonth and year上下文中的内容等于匿名对象中的内容,开始正确生成;而其他人则没有。

所以,我看到一条规则:匿名对象的属性应该是类型string以匹配请求上下文中路由值的类型。

但这条规则似乎不是强制性的, as in Test3,我更改了类型(您现在可能会在上面看到它)并且它仍然可以正常工作。 MVC 设法正确地自行转换类型。


最后我找到了所有这些行为的解释。请看我下面的回答。


这是我用来让它工作的快速解决方法:

public static MvcHtmlString GetPeriodLink(this HtmlHelper html, 
                                          RequestContext context, 
                                          DateTime date)
{
    UrlHelper urlHelper = new UrlHelper(context);

    context.RouteData.Values["month"] = date.Month;
    context.RouteData.Values["year"] = date.Year;

    return MvcHtmlString.Create(
              urlHelper.Action(
                 (string)context.RouteData.Values["action"]));
}

I simply remove the month and year entries from the context.RouteData.Values. I simply replace the month and year entries on the request context. If delete them from context (as I did at first), they would be unavailable for the helpers' methods invoked after this one.

这种方法使我的扩展方法可以按场景工作,如Test 1(请参阅问题)。


FINALY

仔细重读Sanderson S.、Freeman A. - 专业 ASP.NET MVC 3 框架(第三版)我至少找到了所有这些东西的解释:

第 2 部分 ASP.NET MVC 详细信息

第 11 章 URL、路由和区域

生成传出 URL

在部分了解段变量重用:

路由系统将仅重用以下段变量的值: 在 URL 模式中早于提供的任何参数出现 到 Html.ActionLink 方法。

就我而言month-year段之后立即满足controller我确实指定了值month and year,所有尾随段(action,user)不被重复使用。据我没有在匿名对象中指定它们,它们似乎不可用于该路线。因此,route1 无法匹配。

书中甚至有这样的警告:

处理这种行为的最好方法是阻止它 正在发生。我们强烈建议您不要依赖此 行为,并且您为所有段变量提供值 在 URL 模式中。依靠这种行为不仅会让你 代码更难阅读,但你最终会对顺序做出假设 您的用户提出请求,这将 当您的应用程序进入维护阶段时,最终会咬住您。

好吧,它咬了我)))

值得失去 100 次代表来记住(我什至会在这里再次重复)规则:路由系统将仅重用 URL 模式中早于所提供的任何参数出现的段变量的值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

生成传出 URL 时选择了意外的路由 的相关文章

随机推荐

  • 为每个对象 JointJS 创建一个 ToolElement

    我试图为每个对象创建一种工具菜单 当您单击或将鼠标悬停在某个元素上时 它会显示可以执行的几个操作 删除 旋转 放大 链接等 我用过这个question https stackoverflow com questions 30153345 h
  • iOS CoreData批量插入?

    在我的 iPhone 应用程序中 我需要将大约 2000 条记录插入 Core Data 然后用户才能使用该应用程序的任何功能 我正在将记录从本地 JSON 文件加载到 CoreData 中 此过程需要很长时间 2 5 分钟以上 但只需要发
  • 当用户在 Chrome 上按下键盘时,如何检测“删除”和“.”?

    当我按下 它触发了三个事件 keydown keypress and keyup keydown which 190 keyCode 190 keypress which 46 keyCode 46 keyup which 190 keyC
  • AWS Lambda:在运行时获取当前重试尝试计数

    我有一个 AWS Lambda python3 7运行时 运行某个任务 及其MaximumRetryAttempts被设定为1 这意味着它可能会失败一次 然后再尝试一次 由于某种原因 我不会进入 我想知道 在运行时 这是第一次尝试还是第二次
  • Scala:没有明确已知类型参数的类型转换

    考虑以下示例 case class C T x T def f t T println t type ValueType T val list List 1 gt C 2 hello gt C goodbye for a b lt list
  • 后台任务SystemTriggerType.SmsReceived不会触发

    我正在后台 Windows Phone 8 1 中测试任务 但触发器SystemTriggerType SmsReceived不适合我 有人设法让它发挥作用吗 I used 这个例子 http code msdn microsoft com
  • "" 和 " " 之间有什么区别,如何根据字符测试前者?

    在Java中 空引号 和 带有单个空格的引号 之间有什么区别以及如何测试前者char 代表空字符串 is not空 它包含一个空格字符 你不能用以下方式来测试前者char 因为没有字符 用 a 来测试它String 它是String长度为零
  • 获取add_subdirectory添加的CMake项目版本

    我有一个依赖于 Google Test 库的 CMake 项目 我已经使用添加了 Google 测试ExternalProject Add and add subdirectory如此处所述 https github com google
  • jasig cas 重定向过多问题

    我正在尝试使用 spring security 和 spring security cas 带有 Jasig CAS 的 SSO 来保护 spring boot Web 应用程序 尝试访问受保护的资源时 我遇到了太多重定向错误 该项目可用h
  • 使用兆秒差距在不同位置出现多个错误

    我将使用 megaparsec 来解析大学项目的编程语言 但是 我寻找一种报告多个错误的方法 我知道 withRecovery 并且我看到了this https github com mrkkrp megaparsec issues 43问
  • PLSQL:使用合并语句时获取更新与插入的记录数

    无论我的记录如何使用 SQL ROWCOUNT 插入或更新 合并都会始终为您提供合并的记录数 但是如何找出实际插入的记录数与实际更新的记录数 我尝试了这篇文章中的选项 但这似乎不起作用 https asktom oracle com pls
  • Python - 在应用程序中直接显示网络浏览器/iframe

    我有一个脚本 如果对人们回答问题有帮助 那就使用 kivy 我想让它在运行时直接显示 iframe 之类的东西 而不是打开 浏览器 例如这样的事情 def browser url google com iframe url browser
  • 远程连接clearDB heroku数据库

    我如何使用 MySQL 查询浏览器等在 heroku 上远程连接到 ClearDB MySQL 数据库 从哪里获取 url 端口 登录名和密码 在 heroku 网站中 转到 我的应用程序 并选择已安装 ClearDB 的应用程序 在顶角单
  • 稀疏矩阵-矩阵乘法

    我目前正在使用稀疏矩阵 并且必须将稀疏矩阵 矩阵乘法与全矩阵 矩阵乘法的计算时间进行比较 问题是稀疏矩阵计算比全矩阵计算慢得多 我正在使用压缩行存储来压缩我的矩阵 并且将两个矩阵相乘非常耗时 四倍for循环 所以我想知道是否有更好的压缩格式
  • Highchart 日期选择器

    我正在尝试让 jquery ui datepicker 与 highcharts 一起使用 以便用户可以选择一个日期 例如 用户选择 10 月 10 日至 10 月 25 日 用户选择日期后 高图应重新绘制并显示已完成的项目的小时数以及任务
  • jQuery tablesorter 如何找到 sortList 对象

    我正在使用 jQuery 表排序器插件 我想存储用户如何对页面上的表格进行排序 并在下次加载页面时自动按这种方式排序 为此 我首先需要能够找到存储表排序方式的 sortList 对象 对于我的一生 我不知道如何获得它 该文档似乎没有任何相关
  • 尝试让一个对象查看 Unity 中的另一个对象

    嗨 我一直在尝试让对象 Tower 查看对象 Enemy 但是我收到错误 非静态字段 方法或属性 Enemy position 需要对象引用 这是敌人的类 我试图在其中定义敌人的位置以供塔参考 有谁知道如何解决这个问题 谢谢 public
  • 将 SpellCheck.IsEnabled 添加到控件后,WPF 表单加载速度非常慢

    所以我有一个神秘的问题 在我添加之后SpellCheck IsEnabled对于我的其中一个控件 承载该控件的表单需要 3 秒以上的时间才能加载 删除拼写检查属性 表单会立即加载 仅供参考 以下是表格中唯一更改的地方
  • 在 Python 3.5 而不是 2.7 上运行 Django 1.9

    我在 OSX 10 10 上运行 Python 2 7 和 3 5 还有 Django 1 9a 这两个版本都支持 Python 问题是我想在 Python 3 5 而不是 2 7 上运行 Django 在某些线程上 我发现了通过包含 Py
  • 生成传出 URL 时选择了意外的路由

    请考虑以下路线 routes MapRoute route1 controller month year action user routes MapRoute route2 controller month year action 以及以