为什么 StringValue 用于 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对象而不是字符串?

43371 次浏览

Because your query can look like this:

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

And you get all those color values from the one Request.Query["color"] parameter

Request.Query["color"] returns StringValues because collection of string values can be passed. So, I would suggest you to handle this as action parameter, model binding.

public ActionResult SaveColor([FromUri] string[] color);

As already mentioned by others, the type is a StringValues object because technically, multiple values are allowed. While the common practice is to just set a single value, the URI specification does not disallow setting values multiple times. And it’s up to the application to decide how to handle that.

That being said, StringValues has an implicit conversion to string, so you don’t actually need to call ToString() on it, you can just use it as if it was a string. So doing things like Request.Query["color"] == "red", or passing it to a method that expects a string will just work.

And worse, checking for a value to see if a query param is specified can no longer be done like so Request.Query["color"] == null but instead must be checked like so Request.Query["color"].Count == 0

That’s only half true. Yes, in order to check whether a StringValues object is empty, you can check its Count property. You can also check against StringValues.Empty:

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

However, the initial “issue” is that Request.Query[x] will always return a non-null StringValues object (so it’s safe to check for any value). If you want to check whether a key exists in the query arguments, you should use ContainsKey:

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

Or alternatively, use TryGetValue:

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

That all being said, accessing Request.Query is not really necessary most of the times. You should just use make use of model binding instead which will automatically give you the query arguments you need by just having them in the action’s signature:

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

Just posting for curious souls and probably little do with question. Just cautionary note.

I found myself in similar issue. There are couple other issues with this type.

  1. If you have query parameter with no value. For example: /products?pageNo=1&pageSize=

    You will find yourself getting an exception thrown for pageSize parameter as Count property on StringValues will give you value 1, but underlying _value is "" (empty string) and _values is null. Note - Exception happens you are trying to convert or access values from IQueryCollection)

  2. Using TryGetValue will get you value safely out of StringValues but if it is null (like in case of pageSize parameter above), You will have hard time figuring out why can't you convert StringValues to simple String or why can not compare with null to do further operations on it like validations etc.

  3. To do any checking on StringValues type, use methods provided by the type.

To check for null or empty use - StringValues.IsNullOrEmpty(StringValues value)

If you want the first string and are not expecting multiple values just do:

Request.Query["color"][0]