如何使用具有多个同名值的 RouteValues
How to work with RouteValues with multiple values of the same name
在我的 ASP.NET MVC 4 应用程序中,我可以过滤多个标签。在 HTML 中,它看起来像这样:
<form>
<label>
<input type="checkbox" name="tag" value="1">One
</label>
<label>
<input type="checkbox" name="tag" value="2">Two
</label>
<label>
<input type="checkbox" name="tag" value="3">Three
</label>
<input type="submit" name="action" value="Filter">
</form>
选中第一个和第三个复选框时,查询字符串被序列化为 ?tag=1&tag=3
并且我的控制器很好地传递了具有以下类型的对象 class:
// Filter class
public class Filter {
public ICollection<int> tag { get; set; }
}
// Controller method
public ActionResult Index(AdFilter filter)
{
string url = Url.Action("DoFilter", filter);
// url gets this value:
// "/controller/Index?tag=System.Collections.Generic.List%601%5BSystem.Int32%5D"
// I would expect this:
// "/controller/Index?tag=1&tag=3"
...
}
但是,调用 Url.Action
会导致序列化集合的类型名称,而不是实际值。
如何做到这一点?
MVC 的标准基础结构可以处理描述为输入的多键。难道没有可以反过来处理它的标准基础设施吗?我错过了什么吗?
您可以通过以下方式进行:
string url = Url.Action("DoFilter", TypeHelper2.ObjectToDictionary(filter) );
TypeHelper2.ObjectToDictionary
是.NET内部方法的修改版,可以在two file gist.
中找到
更改的行为:当一个项目实现 IEnumerable
时,每个项目都有一个条目
在返回的字典中以 "Name[index]"
作为键创建(索引基于 0)。这是可能的,因为 MVC 控制器的绑定器可以处理 tag=1&tag=3
和 tag[0]=1&tag[1]=3
查询字符串。
一个简单但不那么优雅的解决方案可以是:
public ActionResult Index(AdFilter filter)
{
string parameters = "?";
foreach (var item in filter.tag)
{
parameters += string.Format("tag={0}&", item);
}
//trimming the last "&"
parameters = parameters.TrimEnd(parameters[parameters.Length - 1]);
string url = Url.Action("DoFilter") + parameters;
}
已对以上 2 个答案投赞成票 - 它们都是非常好的答案。在这方面(重定向到一个动作或生成一个 URL 重定向到)MVC 根本没有 "like" 数组。
我可以看到另外两种方法:
1) 使用 TempData 持久化检索数组(例如在 this article 的底部)
2) 为 AdFilter 编写和使用自定义模型绑定器
我自己会选择后者 - 可测试且更具确定性(另外,我不知道进入服务器场场景时 TempData 是如何工作的)
您可能要考虑的另一件事是使用索引操作中的 return 之类的东西。
return View("DoFilter", new AdFilter(){tag = tag});
(这将 return Dofilter 视图,同时在浏览器中保持 "index?tag[0]=1&tag1=2" url)
最后 - 感觉就像您采用过滤条件只是为了将它们发送回浏览器,以便浏览器可以再次询问过滤结果。也许更好的选择是从一开始就将表单上的操作设置为 post 到 "DoFilter":
<form method="post" action="DoFilter">
HTH
您使用接受对象的 Url.Action()
重载来生成路由参数。在内部,这是通过使用反射来构建每个属性名称及其 .ToString()
值的字典来实现的。
在简单值类型的情况下,这可能是 Name = "doekman"
(变为 /DoFilter?Name=doekman
),但在 属性 的情况下,它是一个复杂的对象或集合它 returns 类型名称(即 .ToString()
值)。没有进行递归,这是有道理的,因为查询字符串有长度限制,所以如果对复杂对象和集合进行递归,你很快就会超过限制并抛出异常,如果集合包含很多项目(并且它会创建一个真正的丑陋的查询字符串)。
所以在回答 How can this be done?
你不能,除非你手动生成 RouteValueDictionary
(或编写你自己的助手来生成查询字符串)
在我的 ASP.NET MVC 4 应用程序中,我可以过滤多个标签。在 HTML 中,它看起来像这样:
<form>
<label>
<input type="checkbox" name="tag" value="1">One
</label>
<label>
<input type="checkbox" name="tag" value="2">Two
</label>
<label>
<input type="checkbox" name="tag" value="3">Three
</label>
<input type="submit" name="action" value="Filter">
</form>
选中第一个和第三个复选框时,查询字符串被序列化为 ?tag=1&tag=3
并且我的控制器很好地传递了具有以下类型的对象 class:
// Filter class
public class Filter {
public ICollection<int> tag { get; set; }
}
// Controller method
public ActionResult Index(AdFilter filter)
{
string url = Url.Action("DoFilter", filter);
// url gets this value:
// "/controller/Index?tag=System.Collections.Generic.List%601%5BSystem.Int32%5D"
// I would expect this:
// "/controller/Index?tag=1&tag=3"
...
}
但是,调用 Url.Action
会导致序列化集合的类型名称,而不是实际值。
如何做到这一点?
MVC 的标准基础结构可以处理描述为输入的多键。难道没有可以反过来处理它的标准基础设施吗?我错过了什么吗?
您可以通过以下方式进行:
string url = Url.Action("DoFilter", TypeHelper2.ObjectToDictionary(filter) );
TypeHelper2.ObjectToDictionary
是.NET内部方法的修改版,可以在two file gist.
更改的行为:当一个项目实现 IEnumerable
时,每个项目都有一个条目
在返回的字典中以 "Name[index]"
作为键创建(索引基于 0)。这是可能的,因为 MVC 控制器的绑定器可以处理 tag=1&tag=3
和 tag[0]=1&tag[1]=3
查询字符串。
一个简单但不那么优雅的解决方案可以是:
public ActionResult Index(AdFilter filter)
{
string parameters = "?";
foreach (var item in filter.tag)
{
parameters += string.Format("tag={0}&", item);
}
//trimming the last "&"
parameters = parameters.TrimEnd(parameters[parameters.Length - 1]);
string url = Url.Action("DoFilter") + parameters;
}
已对以上 2 个答案投赞成票 - 它们都是非常好的答案。在这方面(重定向到一个动作或生成一个 URL 重定向到)MVC 根本没有 "like" 数组。
我可以看到另外两种方法:
1) 使用 TempData 持久化检索数组(例如在 this article 的底部)
2) 为 AdFilter 编写和使用自定义模型绑定器
我自己会选择后者 - 可测试且更具确定性(另外,我不知道进入服务器场场景时 TempData 是如何工作的)
您可能要考虑的另一件事是使用索引操作中的 return 之类的东西。
return View("DoFilter", new AdFilter(){tag = tag});
(这将 return Dofilter 视图,同时在浏览器中保持 "index?tag[0]=1&tag1=2" url)
最后 - 感觉就像您采用过滤条件只是为了将它们发送回浏览器,以便浏览器可以再次询问过滤结果。也许更好的选择是从一开始就将表单上的操作设置为 post 到 "DoFilter":
<form method="post" action="DoFilter">
HTH
您使用接受对象的 Url.Action()
重载来生成路由参数。在内部,这是通过使用反射来构建每个属性名称及其 .ToString()
值的字典来实现的。
在简单值类型的情况下,这可能是 Name = "doekman"
(变为 /DoFilter?Name=doekman
),但在 属性 的情况下,它是一个复杂的对象或集合它 returns 类型名称(即 .ToString()
值)。没有进行递归,这是有道理的,因为查询字符串有长度限制,所以如果对复杂对象和集合进行递归,你很快就会超过限制并抛出异常,如果集合包含很多项目(并且它会创建一个真正的丑陋的查询字符串)。
所以在回答 How can this be done?
你不能,除非你手动生成 RouteValueDictionary
(或编写你自己的助手来生成查询字符串)