Подтвердить что ты не робот

По умолчанию ASP.NET MVC QueryString переопределяет заданные значения?

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

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, поскольку запросы с настройками по умолчанию не являются точно эзотерическими или крайними -case, поэтому почти наверняка моя вина. Любые идеи, что я делаю неправильно?

4b9b3361

Ответ 1

Лучший способ взглянуть на 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

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

Ответ 2

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

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

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

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


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


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

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

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

Ответ 3

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

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

Я справился с этой проблемой, выполнив то, что предложил Дейл-Раган и применил к ней в методе действий. Работает для меня.

Ответ 4

Я думал, что точка с Routing in MVC состоит в том, чтобы избавиться от querystrings. Вот так:

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