使用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框架的测试版中,因为带有默认值的查询字符串并不完全是一个深奥或边缘的功能,所以这几乎肯定是我的错.我有什么想法我做错了吗?
使用QueryStrings查看ASP.NET MVC的最佳方法是将它们视为路由不知道的值.正如您所发现的,QueryString不是RouteData的一部分,因此,您应该将您传递的内容保存为与路由值分开的查询字符串.
解决这些问题的一种方法是,如果从QueryString传递的值为null,则自己在操作中创建默认值.
在您的示例中,路由知道x,因此您的url应该看起来像这样:
/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
希望这有助于澄清一些事情.
我的一位同事找到了一个链接,表明这是设计的,看来该文章的作者提出了MVC团队的一个问题,说这是对早期版本的改变.它们的响应如下(对于"页面",您可以读取"x"以使其与上述问题相关):
这是设计的.路由与查询字符串值无关; 它仅关注来自RouteData的值.您应该从Defaults字典中删除"page"条目,并在action方法本身或过滤器中设置"page"的默认值(如果尚未设置).
我们希望将来能够更简单地将参数标记为显式来自RouteData,查询字符串或表单.在实施之前,上述解决方案应该有效.如果没有,请告诉我们!
因此看起来这种行为是"正确的",但它与最不惊讶的原则是如此正交,我仍然不能完全相信它.
编辑#1:请注意,帖子详细说明了如何提供默认值的方法,但是这不再有效,因为ActionMethod
他用来访问的属性MethodInfo
已在最新版本的ASP.NET MVC中删除.我正在开发一种替代方案,并在完成后发布.
编辑#2:我已经更新了链接帖子中的想法,以便与ASP.NET MVC的Preview 5版本一起使用,我相信它也应该与Beta版本一起工作,但我不能保证它,因为我们没有移动到那个版本了.这很简单,我刚刚在这里发布了它.
首先是默认属性(我们不能使用现有的.NET,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); } }
然后你可以简单地将它应用于查询字符串中的方法参数,例如
public ActionResult Foo([Default(1)] int x) { // implementation }
奇迹般有效!