【问题标题】:How to work with RouteValues with multiple values of the same name如何使用具有多个同名值的 RouteValues
【发布时间】:2015-02-12 16:02:39
【问题描述】:

在我的 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&amp;tag=3,我的控制器很好地传递了一个具有以下类类型的对象:

// 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 的标准基础架构可以处理描述为输入的多键。是否没有标准的基础设施可以反过来处理它?我错过了什么吗?

【问题讨论】:

  • 感谢您的回答,但这两个答案对我来说似乎都是一种黑客行为。我的印象是我做错了什么。 RouteValueDictionary 不能使用“多个”(&lt;select multiple&gt;,在我的情况下是 &lt;input type="checkbox"&gt; 具有相同名称)值吗?很难想象微软提出了一个没有包含整个 HTML FORM 模型的抽象......

标签: c# asp.net-mvc-4 routevalues


【解决方案1】:

您可以通过以下方式进行:

string url = Url.Action("DoFilter", TypeHelper2.ObjectToDictionary(filter) );

TypeHelper2.ObjectToDictionary 是 .NET 内部方法的修改版本,可以在此two file gist 中找到。

改变的行为:当一个项目实现IEnumerable时,每个项目都有一个条目 在返回的字典中创建,作为键 "Name[index]"(索引从 0 开始)。这是可能的,因为 MVC 控制器的绑定器可以处理 tag=1&amp;tag=3tag[0]=1&amp;tag[1]=3 查询字符串。

【讨论】:

  • 我不知道控制器处理 QueryStrings ?tag=1&amp;tag=3 和 ?tag[0]=1&tag[1]=3` 是一样的。内部Url.Actionobject 转换为RouteValueDictionary。这不是问题,因为字典的值仍然是IEnumerable。但是,这也是一个简单的解决方案,因为在此转换中,您可以使用它自己的键在字典中添加一个项目。这个解决方案非常透明,只需要从 MVC 项目复制两个文件(一个文件略有更改)。我应该编辑这个答案吗,因为这个答案引导我找到这个解决方案?
  • 这是我制作的2 file-gist 解决方案。
【解决方案2】:

一个简单但不那么优雅的解决方案可以是:

  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;

  }

【讨论】:

  • 我也喜欢这个答案,但它有点老套,可能很难正确。项目需要编码,而且这段代码没有考虑书签。。不过话说回来,它不是那么优雅,但是当有问题时,很容易调试这段代码。
【解决方案3】:

已对上述 2 个答案投了赞成票 - 它们都是非常好的答案。在这方面(重定向到操作或生成要重定向到的 URL)MVC 根本不“喜欢”数组。

我可以看到另外两种方法:

1) 使用 TempData 持久化检索数组(例如在this article 的底部)

2) 为 AdFilter 编写和使用自定义模型绑定器

我自己会选择后者 - 可测试且更具确定性(另外,当您进入服务器场场景时,我不知道 TempData 是如何工作的)

您可能要考虑的另一件事是使用诸如从 Index 操作返回的内容。

return View("DoFilter", new AdFilter(){tag = tag});

(这将返回 Dofilter 视图,同时在浏览器中保持 "index?tag[0]=1&tag1=2" url)

最后 - 感觉就像您采用过滤条件只是为了将它们发送回浏览器,以便浏览器可以再次询问过滤结果。也许更好的选择是将表单上的操作设置为从一开始就发布到“DoFilter”:

<form  method="post" action="DoFilter"> 

HTH

【讨论】:

  • 就我而言,我需要一个查询字符串,因为我使用的是PagedList。 (顺便说一句:我实际上赞成答案,因为答案很好。但是,我的问题是关于 MVC 处理数组)。
  • 啊-好的-从您的问题中没有完全明白这一点(感谢您的支持!)。本质上,如果您需要一个可点击的 URL(通过寻呼机上的链接使用 GET?),那么您正在尝试复制“当表单发布到 url 时”(来自您的第一个示例),并执行“只用一个基本的 url”——这不会飞——MVC 不能自然地处理这种事情(见其他答案)。如果是我,并且我必须支持这一点,那么我肯定会创建一个自定义模型绑定器并在进入的过程中使用它(所以你有一个 PagedAdFilterModel 和一个用于将 HTTPGET 拉成碎片的绑定器)。 HTH
【解决方案4】:

您使用接受对象的Url.Action() 的重载来生成路由参数。在内部,这是通过使用反射来构建每个属性名称及其.ToString() 值的字典。

在简单值类型的情况下,这可能是Name = "doekman"(变成/DoFilter?Name=doekman),但在属性是复杂对象或集合的情况下,它返回类型名(即.ToString()价值)。没有进行递归,这是有道理的,因为查询字符串有长度限制,所以如果对复杂的对象和集合进行递归,如果集合包含很多项目,你很快就会超过限制并抛出异常(它会创建一个真正的丑陋的查询字符串)。

所以回答How can this be done? 你不能,除非你手动生成RouteValueDictionary(或者编写你自己的帮助器来生成查询字符串)

【讨论】:

  • 另一种选择是编写我自己的Url.Action(),它也可以处理IEnumerableUrl.Action() 已经开源了吗?我在 GitHub 上找不到...
  • 是的(这就是我创建你自己的助手的意思)。目前UrlHelper的源码是here,大家可以下载。
猜你喜欢
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-03
相关资源
最近更新 更多