2009-01-19 70 views
25

使用ASP.NET MVC预览版5(虽然这也已经在Beta版中试过了),但似乎查询字符串中的querystring默认值会覆盖传入的值。甲REPRO是编写这样的控制器:ASP.NET MVC的QueryString默认覆盖提供的值?

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 

这对我来说就像一个错误,但我会觉得很奇怪,像这样的错误仍然可以在ASP.NET MVC框架的测试版中发现,因为具有默认值的querystrings并不完全是一个深奥的或边缘特征,所以它几乎肯定是我的错。任何想法我做错了什么?

+0

对于这些情况,堆栈溢出需要“投票供应商推迟”按钮。 – John 2015-07-28 15:09:11

回答

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}); 

为了让您的行为正在寻找。

如果你想传递一个查询字符串值,说喜欢页码,那么你这样做:

/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 

希望这有助于澄清一些事情。

-3

我认为与MVC中的路由点​​是摆脱querystrings。就像这样:

routes.MapRoute(
    "test", 
    "Test/Foo/{x}", 
    new { controller = "Test", action = "Foo", x = 1 }); 
+0

查询字符串在查询时是合适的,例如你可能有可选的参数,如排序,页面等。无论如何,这并不能以任何方式回答这个问题。 – 2009-01-19 17:29:37

+0

@Greg Beech:这个问题应该至少提供一个例子来说明这一点。 – Spoike 2009-01-19 19:16:35

15

我的一个同事发现a link which indicates that this is by design,看来,文章raised an issue with the MVC team说,这是从早期版本的改变的作者。他们的回应是低于(对于“页面”,你可以读到“x”与上述问题有关):

这是设计。路由不涉及 与查询字符串 值有关;它仅关注来自RouteData的 值。你应该 改为移除默认字典“页” 的条目,在 无论是操作方法本身或 过滤器设置 “页”的默认值,如果它尚未设置。

我们希望在未来有一个 更简单的方式来标记参数作为 从的RouteData中, 查询字符串,或一个形式明确地到来。直到 实施上述解决方案应该 工作。请让我们知道,如果它 不!

所以看起来这种行为是“正确的”,但它与principle of least astonishment如此正交,我仍然不能相信它。


编辑#1:请注意,后详细说明了如何提供默认值,但是这不再工作,因为他用来访问MethodInfo已经在ASP的最新版本中删除了ActionMethod性能的方法。 NET MVC。我目前正在研究替代方案,并在完成后发布。


编辑#2:我已经更新了观念在链接后与预览5发布ASP.NET MVC的工作,我相信也应该与Beta版本的工作,虽然我不能保证因为我们还没有转移到那个版本。这很简单,我只是把它贴在这里。

首先有默认的属性(我们不能因为它需要从CustomModelBinderAttribute继承使用现有的.NET DefaultValueAttribute):

[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 
} 

工程就像一个魅力!

+0

grr感谢您的解决方案,这让我烦恼! – 2009-06-04 15:25:20

0

我认为查询字符串参数不会覆盖默认值的原因是为了阻止黑客入侵网址。

有人可以使用一个URL,其查询字符串包括控制器,操作或其他您不希望它们更改的默认值。

我已经通过做@ Dale-Ragan建议并在操作方法中处理它来处理这个问题。适用于我。