使用ASP.NET MVC Preview 5(尽管已经尝试使用Beta),路由中的查询字符串默认值会覆盖查询字符串中传递的值。一个repro就是写一个像这样的控制器:
public class TestController : Controller
{
public ActionResult Foo(int x)
{
Trace.WriteLine(x);
Trace.WriteLine(this.HttpContext.Request.QueryString["x"]);
return new EmptyResult();
}
}
路由映射如下:
routes.MapRoute(
"test",
"Test/Foo",
new { controller = "Test", action = "Foo", x = 1 });
然后使用此相对URI调用它:
/Test/Foo?x=5
我看到的跟踪输出是:
1
5
因此换句话说,为路径设置的默认值总是传递给方法,而不管它是否实际上是在查询字符串上提供的。请注意,如果删除了查询字符串的默认值,即路由映射如下:
routes.MapRoute(
"test",
"Test/Foo",
new { controller = "Test", action = "Foo" });
然后控制器按预期运行,并将值作为参数值传入,给出跟踪输出:
5
5
这对我来说就像一个bug,但我会发现非常令人惊讶的是,像这样的bug仍然可以在ASP.NET MVC框架的beta版本中,因为具有默认值的查询字符串并不完全是一个深奥或边缘 - 功能,所以这几乎肯定是我的错。我有什么想法吗?
答案 0 :(得分:30)
使用QueryStrings查看ASP.NET MVC的最佳方法是将它们视为路由不知道的值。如您所知,QueryString不是RouteData的一部分,因此,您应该将您传递的内容保存为与路由值分开的查询字符串。
解决这些问题的一种方法是,如果从QueryString传递的值为null,则自己在操作中自己创建默认值。
在您的示例中,路由知道x,因此您的网址应该如下所示:
/Test/Foo or /Test/Foo/5
并且路线应如下所示:
routes.MapRoute("test", "Test/Foo/{x}", new {controller = "Test", action = "Foo", x = 1});
获得您正在寻找的行为。
如果你想传递一个QueryString值,比如一个页码,那你可以这样做:
/Test/Foo/5?page=1
你的行动应该像这样改变:
public ActionResult Foo(int x, int? page)
{
Trace.WriteLine(x);
Trace.WriteLine(page.HasValue ? page.Value : 1);
return new EmptyResult();
}
现在测试:
Url: /Test/Foo Trace: 1 1 Url: /Test/Foo/5 Trace: 5 1 Url: /Test/Foo/5?page=2 Trace: 5 2 Url: /Test/Foo?page=2 Trace: 1 2
希望这有助于澄清一些事情。
答案 1 :(得分:15)
我的一位同事发现a link which indicates that this is by design,该文章的作者raised an issue with the MVC team表示这是对早期版本的更改。他们的反应如下(对于“页面”,您可以阅读“x”以使其与上述问题相关):
这是设计的。路由没有 关注查询字符串 值;它仅关注自身 来自RouteData的值。你应该 而是删除“页面”的条目 来自Defaults字典,以及 动作方法本身或在 过滤器设置默认值 “page”如果尚未设置。
我们希望将来有一个 将参数标记为更简单的方法 显然来自RouteData, 查询字符串或表单。在那之前 实施上述解决方案应该 工作。如果有,请告诉我们 不!
所以看起来这种行为是'正确的',但是它与principle of least astonishment非常正交,我仍然不能相信它。
编辑#1:请注意,该帖子详细说明了如何提供默认值的方法,但是这不再有效,因为他用来访问ActionMethod
的{{1}}属性已被删除ASP.NET MVC的版本。我目前正在开发替代方案,并在完成后发布。
编辑#2:我已经更新了链接帖子中的想法,以便与ASP.NET MVC的Preview 5版本一起使用,我相信它也应该与Beta版本一起使用,虽然我不能保证它,因为我们没有尚未转移到那个版本。这很简单,我刚刚在这里发布了它。
首先是默认属性(我们不能使用现有的.NET MethodInfo
,因为它需要从DefaultValueAttribute
继承):
CustomModelBinderAttribute
自定义活页夹:
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class DefaultAttribute : CustomModelBinderAttribute
{
private readonly object value;
public DefaultAttribute(object value)
{
this.value = value;
}
public DefaultAttribute(string value, Type conversionType)
{
this.value = Convert.ChangeType(value, conversionType);
}
public override IModelBinder GetBinder()
{
return new DefaultValueModelBinder(this.value);
}
}
然后你可以简单地将它应用于查询字符串中的方法参数,例如。
public sealed class DefaultValueModelBinder : IModelBinder
{
private readonly object value;
public DefaultValueModelBinder(object value)
{
this.value = value;
}
public ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
var request = bindingContext.HttpContext.Request;
var queryValue = request .QueryString[bindingContext.ModelName];
return string.IsNullOrEmpty(queryValue)
? new ModelBinderResult(this.value)
: new DefaultModelBinder().BindModel(bindingContext);
}
}
像魅力一样!
答案 2 :(得分:0)
我认为查询字符串参数不会覆盖默认值的原因是阻止人们破解网址。
有人可以使用其查询字符串包含控制器,操作或其他默认值的网址,您不希望它们更改。
我通过做@ Dale-Ragan建议并在action方法中处理它来处理这个问题。适合我。
答案 3 :(得分:-3)
我认为MVC中的Routing是要摆脱查询字符串。像这样:
routes.MapRoute(
"test",
"Test/Foo/{x}",
new { controller = "Test", action = "Foo", x = 1 });