Почему StringValues ​​используется для значений Request.Query?

Допустим, у меня есть URL-адрес, который выглядит следующим образом: www.myhost.com/mypage?color=blue

В Asp.Net Core я ожидаю получить значение параметра цветового запроса, выполнив следующие действия:

string color = Request.Query["color"];

Но оказывается, что Request.Query["color"] возвращает значение типа StringValues, а не string. Почему это так?

Очевидно, тип StringValues может содержать массив строк и включает поддержку неявного преобразования в string[], что здорово, но зачем это нужно для значения параметра запроса?

Необходимость получить такое значение кажется странным:

string color = Request.Query["color"].ToString();

И что еще хуже, проверка значения, чтобы определить, задан ли параметр запроса, больше не может быть выполнена так

  if(Request.Query["color"] == null) { 
      //param was not specified
  }

но вместо этого нужно проверить, как это так

 if(Request.Query["color"].Count == 0) { 
      //param was not specified
 }

Поскольку один параметр запроса не может иметь несколько значений (насколько я знаю), почему Request.Query["color"] возвращает объект StringValues, а не строку?

Ответ 1

Потому что ваш запрос может выглядеть так:

www.myhost.com/mypage?color=blue&color=red&color=yellow

И вы получите все эти значения color из одного параметра Request.Query["color"]

Ответ 2

Как уже упоминалось другими, тип является объектом StringValues, поскольку технически допускается несколько значений. Хотя обычной практикой является просто установить одно значение, спецификация URI не запрещает установку значений несколько раз. И это до приложения, чтобы решить, как с этим справиться.

При этом StringValues имеет неявное преобразование в string, поэтому вам не нужно называть ToString() на нем, вы можете просто использовать его, как если бы это была строка. Так что делать такие вещи, как Request.Query["color"] == "red", или передавать его методу, который ожидает, что строка будет работать.

И хуже того, проверка значения для того, чтобы увидеть, указан ли параметр запроса, больше не может быть выполнена так Request.Query["color"] == null, но вместо этого нужно проверить так: Request.Query["color"].Count == 0

Это только половина истины. Да, чтобы проверить, является ли объект StringValues пустым, вы можете проверить его свойство Count. Вы также можете проверить StringValues.Empty:

Request.Query["color"] == StringValues.Empty

Однако исходная "проблема" заключается в том, что Request.Query[x] будет всегда возвращать ненулевой объект StringValues (чтобы его можно было проверить на любое значение). Если вы хотите проверить, существует ли ключ в аргументах запроса, вы должны использовать ContainsKey:

if (Request.Query.ContainsKey("color"))
{
    // only now actually retrieve the value
    string colorValue = Request.Query["color"];
}

Или, наоборот, используйте TryGetValue:

if (Request.Query.TryGetValue("color", out var colorValue))
{
    DoSomething(colorValue);
}

Чтобы все сказанное, доступ к Request.Query на самом деле не нужен в большинстве случаев. Вы должны просто использовать вместо привязку к модели, которая автоматически предоставит вам необходимые аргументы запроса, просто используя их в сигнатуре действий:

public ActionResult MyAction(string color)
{
    DoSomething(color);
}

Ответ 3

Нельзя использовать Request.Query,

public ActionResult SaveColor(string color);

Пусть Mvc обрабатывает его.

Ответ 4

Просто пост для любопытных душ и, вероятно, мало что делать с вопросом. Просто предупреждающая заметка.

Я оказался в подобной проблеме. Есть несколько других проблем с этим типом.

  1. Если у вас есть параметр запроса без значения. Например: /products?pageNo=1&pageSize=

    Вы получите исключение для параметра pageSize как Count свойство в StringValue даст вам значение 1, но в основе _value лежит "" (пусто) строка) и _values - это null. Примечание. Исключение - вы пытаетесь конвертировать или доступ к значениям из IQueryCollection)

  2. Использование TryGetValue безопасно вытащит вас из StringValue, но если оно будет нулевым (как в случае с параметром pageSize выше), вам будет сложно понять, почему Вы не можете преобразовать StringValue в простой String или почему не можете сравнить с null в выполнять дальнейшие операции над ним, такие как проверки и т.д.

  3. Чтобы выполнить любую проверку типа StringValue, используйте методы, предоставляемые этим типом.

Для проверки на нулевое или пустое использование - StringValue.IsNullOrEmpty(StringValue value)