26

使用 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

这在我看来像一个错误,但我会发现像这样的错误仍然存​​在于 ASP.NET MVC 框架的 beta 版本中,这让我感到非常惊讶,因为具有默认值的查询字符串并不完全是一个深奥或边缘情况的功能,所以几乎可以肯定是我的错。任何想法我做错了什么?

4

4 回答 4

31

使用 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

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

于 2009-01-19T18:36:09.193 回答
15

我的一位同事发现了一个链接,该链接表明这是设计使然,并且该文章的作者似乎向MVC 团队提出了一个问题,称这是对早期版本的更改。他们的回复如下(对于“页面”,您可以阅读“x”以使其与上述问题相关):

这是设计使然。路由本身不关心查询字符串值;它只关注来自 RouteData 的值。相反,您应该从 Defaults 字典中删除“page”条目,并在操作方法本身或过滤器中设置“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
}

奇迹般有效!

于 2009-01-19T17:34:49.910 回答
0

我认为查询字符串参数不覆盖默认值的原因是阻止人们破解 url。

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

我通过执行@Dale-Ragan 的建议并在操作方法中处理它来解决这个问题。为我工作。

于 2009-10-01T10:06:29.773 回答
-3

我认为 MVC 中路由的重点是摆脱查询字符串。像这样:

routes.MapRoute(
    "test",
    "Test/Foo/{x}",
    new { controller = "Test", action = "Foo", x = 1 });
于 2009-01-19T17:27:00.707 回答