UrlHelper.RouteUrl( routeName, RouteValueDictionary ) returns 空
UrlHelper.RouteUrl( routeName, RouteValueDictionary ) returns null
我在 .NET 4.6.1 上有一个 ASP.NET MVC 5 Web 应用程序 运行。
我的应用程序中有一个名为 "Foo" 的区域,在我的区域注册中我添加了一个命名路线,如下所示:
RegisterArea(AreaRegistrationContext context) {
context.MapRoute("Foo_Bar", "{tenantName}/foo/{fooId}/{fooRevision}", new { controller = "Foo", action = "Index" } );
}
从我的 Razor 视图代码中,我想使用此路由生成一个 link,所以我这样做:
RouteValueDictionary rd = new RouteValueDictionary( this.Url.RequestContext.RouteData.Values );
rd["fooId" ] = "123";
rd["fooRevision"] = "4";
this.Url.RouteUrl("Foo_Bar", rd );
然而这个returns无效。没有例外。没有其他副作用。
调试器显示 rd
包含 5 个命名值:tenantName
、controller
、action
、fooId
和 fooRevision
。我还在调试期间添加了 area
只是为了确保,但它仍然是 returns null.
我验证了该路由存在,因为 this.Url.RouteCollection["Foo_Bar"]
returns System.Web.Routing.Route
对象对应于该路由。
我尝试了 .NET Source Stepping,我能够将 1 级步进到 UrlHelper
,但是步进到 GenerateUrl
函数失败并且 Visual Studio 2015 U2 总是步过它。
A null
值表示请求的值不匹配任何路由。由于您还明确指定了路由名称(其作用类似于路由 table 上的过滤器),因此可以肯定地说路由 Foo_Bar
与提供的路由值输入不匹配。
您没有为 {tenantName}
提供默认值(如果没有合理的默认值也没关系),因此 {tenantName}
是必填值。当您为 {tenantName}
提供值时,会正确生成 URL。
RouteValueDictionary rd1 = new RouteValueDictionary();
rd1["fooId"] = "123";
rd1["fooRevision"] = "4";
var url1 = this.Url.RouteUrl("Foo_Bar", rd1);
// url1 value is null
RouteValueDictionary rd2 = new RouteValueDictionary();
rd2["fooId"] = "123";
rd2["fooRevision"] = "4";
rd2["tenantName"] = "SomeTenant";
var url2 = this.Url.RouteUrl("Foo_Bar", rd2);
// url2 value is /SomeTenant/foo/123/4
NOTE: MVC automatically reuses route values from the current request that are not provided explicitly. There is no need to explicitly add this.Url.RequestContext.RouteData.Values
to the route collection.
当然,这意味着您可能不必显式地将 tenantName
添加到路由集合中,具体取决于您 运行 此代码所在的上下文是否已经包含它。
我不得不进入 .NET Framework 源代码来解决这个问题 - 首先进入 System.Web.Mvc.dll
进入 UrlHelper.RouteUrl
,然后调用 System.Web.Routing.dll
(这是现在是 .NET 4.5 中 System.Web.dll
的存根)。
答案就在ParsedRoute::Bind
( http://referencesource.microsoft.com/#System.Web/Routing/ParsedRoute.cs )
所以问题出在这一行:
RouteValueDictionary rd = new RouteValueDictionary( this.Url.RequestContext.RouteData.Values );
这导致 rd
从当前请求上下文中复制(继承)值,我认为这是可取的。我认为因为路由 Foo_Bar
提供了默认的 controller
和 action
参数值,所以它总是优先使用那些 rd
中的参数值,但我错了.
传递给 UrlHelper.RouteUrl
的 rd
包含 controller
和 action
的值,但它们是针对当前请求的,因此与默认值不同。
ParsedRoute::Bind
的定义是这样的,如果提供的 RouteValueDictionary
提供了与默认值不同的参数值,它就会中止(请参阅 ParsedRoute.cs
.[=29 中的第 133 到 149 行) =]
我觉得框架应该在那种情况下抛出异常,而不是返回 null - 因为这种行为似乎没有很好的记录 - 特别是因为所有搜索结果都说问题只是路由没有不存在或传递的参数不足 - 在我的例子中,问题是在某种程度上传递了太多参数。
我在 .NET 4.6.1 上有一个 ASP.NET MVC 5 Web 应用程序 运行。
我的应用程序中有一个名为 "Foo" 的区域,在我的区域注册中我添加了一个命名路线,如下所示:
RegisterArea(AreaRegistrationContext context) {
context.MapRoute("Foo_Bar", "{tenantName}/foo/{fooId}/{fooRevision}", new { controller = "Foo", action = "Index" } );
}
从我的 Razor 视图代码中,我想使用此路由生成一个 link,所以我这样做:
RouteValueDictionary rd = new RouteValueDictionary( this.Url.RequestContext.RouteData.Values );
rd["fooId" ] = "123";
rd["fooRevision"] = "4";
this.Url.RouteUrl("Foo_Bar", rd );
然而这个returns无效。没有例外。没有其他副作用。
调试器显示 rd
包含 5 个命名值:tenantName
、controller
、action
、fooId
和 fooRevision
。我还在调试期间添加了 area
只是为了确保,但它仍然是 returns null.
我验证了该路由存在,因为 this.Url.RouteCollection["Foo_Bar"]
returns System.Web.Routing.Route
对象对应于该路由。
我尝试了 .NET Source Stepping,我能够将 1 级步进到 UrlHelper
,但是步进到 GenerateUrl
函数失败并且 Visual Studio 2015 U2 总是步过它。
A null
值表示请求的值不匹配任何路由。由于您还明确指定了路由名称(其作用类似于路由 table 上的过滤器),因此可以肯定地说路由 Foo_Bar
与提供的路由值输入不匹配。
您没有为 {tenantName}
提供默认值(如果没有合理的默认值也没关系),因此 {tenantName}
是必填值。当您为 {tenantName}
提供值时,会正确生成 URL。
RouteValueDictionary rd1 = new RouteValueDictionary();
rd1["fooId"] = "123";
rd1["fooRevision"] = "4";
var url1 = this.Url.RouteUrl("Foo_Bar", rd1);
// url1 value is null
RouteValueDictionary rd2 = new RouteValueDictionary();
rd2["fooId"] = "123";
rd2["fooRevision"] = "4";
rd2["tenantName"] = "SomeTenant";
var url2 = this.Url.RouteUrl("Foo_Bar", rd2);
// url2 value is /SomeTenant/foo/123/4
NOTE: MVC automatically reuses route values from the current request that are not provided explicitly. There is no need to explicitly add
this.Url.RequestContext.RouteData.Values
to the route collection.
当然,这意味着您可能不必显式地将 tenantName
添加到路由集合中,具体取决于您 运行 此代码所在的上下文是否已经包含它。
我不得不进入 .NET Framework 源代码来解决这个问题 - 首先进入 System.Web.Mvc.dll
进入 UrlHelper.RouteUrl
,然后调用 System.Web.Routing.dll
(这是现在是 .NET 4.5 中 System.Web.dll
的存根)。
答案就在ParsedRoute::Bind
( http://referencesource.microsoft.com/#System.Web/Routing/ParsedRoute.cs )
所以问题出在这一行:
RouteValueDictionary rd = new RouteValueDictionary( this.Url.RequestContext.RouteData.Values );
这导致 rd
从当前请求上下文中复制(继承)值,我认为这是可取的。我认为因为路由 Foo_Bar
提供了默认的 controller
和 action
参数值,所以它总是优先使用那些 rd
中的参数值,但我错了.
传递给 UrlHelper.RouteUrl
的 rd
包含 controller
和 action
的值,但它们是针对当前请求的,因此与默认值不同。
ParsedRoute::Bind
的定义是这样的,如果提供的 RouteValueDictionary
提供了与默认值不同的参数值,它就会中止(请参阅 ParsedRoute.cs
.[=29 中的第 133 到 149 行) =]
我觉得框架应该在那种情况下抛出异常,而不是返回 null - 因为这种行为似乎没有很好的记录 - 特别是因为所有搜索结果都说问题只是路由没有不存在或传递的参数不足 - 在我的例子中,问题是在某种程度上传递了太多参数。