当前位置:  开发笔记 > 后端 > 正文

ASP.NET MVC QueryString默认覆盖提供的值?

如何解决《ASP.NETMVCQueryString默认覆盖提供的值?》经验,为你挑选了2个好方法。

使用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框架的测试版中,因为带有默认值的查询字符串并不完全是一个深奥或边缘的功能,所以这几乎肯定是我的错.我有什么想法我做错了吗?



1> Dale Ragan..:

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

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



2> Greg Beech..:

我的一位同事找到了一个链接,表明这是设计的,看来该文章的作者提出了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
}

奇迹般有效!

推荐阅读
女女的家_747
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有