ASP.NET MVC QueryString по умолчанию переопределяет предоставленные значения? - PullRequest
25 голосов
/ 19 января 2009

При использовании ASP.NET MVC Preview 5 (хотя это было также опробовано в бета-версии), кажется, что значения по умолчанию для строки запроса переопределяют значение, переданное в строке запроса. 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 framework, поскольку строки запросов со значениями по умолчанию не являются эзотерическими особенность, так что это почти наверняка моя вина. Есть идеи, что я делаю не так?

Ответы [ 4 ]

30 голосов
/ 19 января 2009

Лучший способ взглянуть на ASP.NET MVC с QueryStrings - это воспринимать их как значения, о которых маршрут не знает. Как вы узнали, QueryString не является частью RouteData, поэтому вы должны хранить то, что вы передаете, в виде строки запроса отдельно от значений маршрута.

Способ обойти их - самостоятельно создать значения по умолчанию в действии, если значения, переданные из QueryString, равны нулю.

В вашем примере маршрут знает о 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

Надеюсь, это поможет прояснить некоторые вещи.

15 голосов
/ 19 января 2009

Один из моих коллег нашел ссылку, которая указывает на то, что это было задумано , и кажется, что автор этой статьи поднял проблему с командой MVC , сказав, что это было изменение по сравнению с более ранние выпуски. Ответ от них был ниже (для «страницы» вы можете прочитать «х», чтобы он имел отношение к вопросу выше):

Это по замыслу. Маршрутизация не займитесь строкой запроса ценности; это касается только значения из RouteData. Вам следует вместо этого удалите запись для "страницы" из словаря по умолчанию, и в либо сам метод действия или в фильтр устанавливает значение по умолчанию для "страница", если она еще не установлена.

Мы надеемся, что в будущем более простой способ пометить параметр как явно из RouteData, Строка запроса или форма. Пока это Реализованное выше решение должно Работа. Пожалуйста, дайте нам знать, если это нет!

Итак, похоже, что это «правильное» поведение, однако оно настолько ортогонально принципу наименьшего удивления , что я до сих пор не могу в это поверить.


Редактирование # 1: обратите внимание, что в сообщении описан метод предоставления значений по умолчанию, однако он больше не работает, поскольку свойство ActionMethod, которое он использует для доступа к MethodInfo, было удалено в последней версии ASP. NET MVC. В настоящее время я работаю над альтернативой и опубликую ее, когда все будет готово.


Редактирование # 2: я обновил идею в связанном посте, чтобы она работала с выпуском Preview 5 ASP.NET MVC, и я верю, что он должен работать и с бета-версией, хотя я не могу гарантировать это, поскольку у нас нет еще не перешел на этот релиз. Это так просто, что я только что разместил это здесь.

Сначала есть атрибут по умолчанию (мы не можем использовать существующий .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
}

Работает как шарм!

0 голосов
/ 01 октября 2009

Я думаю, что причина, по которой параметры строки запроса не переопределяют значения по умолчанию, заключается в том, чтобы не дать людям взломать URL.

Кто-то может использовать URL-адрес, строка запроса которого содержит контроллер, действие или другие значения по умолчанию, которые вы не хотите, чтобы они меняли.

Я справился с этой проблемой, выполнив то, что предложил @ Dale-Ragan, и применил ее в методе действия. У меня работает.

0 голосов
/ 19 января 2009

Я думал, что с помощью Routing в MVC важно избавиться от строк запросов. Как это:

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