如何使用具有多个同名值的 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=3tag[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 (或编写你自己的助手来生成查询字符串)