【问题标题】:MVC Model Binding to a collection where collection does not begin with a 0 indexMVC 模型绑定到集合不以 0 索引开头的集合
【发布时间】:2012-06-01 08:49:46
【问题描述】:

我正在尝试对集合中某个项目的属性执行远程验证。验证适用于集合的第一项。验证方法的 http 请求如下所示:

/Validation/IsImeiAvailable?ImeiGadgets[0].ImeiNumber=123456789012345

但是,在 url 如下所示的第二个项目中,验证不起作用

/Validation/IsImeiAvailable?ImeiGadgets[1].ImeiNumber=123456789012345

现在我很确定这是因为绑定不会在不以零索引开头的集合上起作用。

我的验证方法的签名如下:

public JsonResult IsImeiAvailable([Bind(Prefix = "ImeiGadgets")] Models.ViewModels.ImeiGadget[] imeiGadget)

因为我在集合中传递一个项目,所以我必须像这样绑定,但我真正传递的只是一个值。

除了将其绑定为普通的旧查询字符串之外,我是否可以处理这个问题。

谢谢

编辑:这是获取 Imei 变量的快速修复方法,但我宁愿使用模型绑定:

string imeiNumber = Request.Url.AbsoluteUri.Substring(Request.Url.AbsoluteUri.IndexOf("=")+1);

编辑:这是我的 ImeiGadget 类:

public class ImeiGadget
{
    public int Id { get; set; }

    [Remote("IsImeiAvailable", "Validation")]
    [Required(ErrorMessage = "Please provide the IMEI Number for your Phone")]
    [RegularExpression(@"(\D*\d){15,17}", ErrorMessage = "An IMEI number must contain between 15 & 17 digits")]
    public string ImeiNumber { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
}

【问题讨论】:

  • 这是aspx吗?如果是,请重新标记
  • 修复了标记——当我看到 [Bind] attr 时假定为 aspnet-mvc
  • 您是通过 Ajax 请求发布的吗?如果是这样,我们可以看到该代码吗?我有预感:-)
  • 这是一个 MVC3 问题。我标记错了吗?
  • 我通过 Ajax 请求发布,该请求由 MVC3 的远程验证功能处理

标签: asp.net-mvc asp.net-mvc-3 validation model-binding


【解决方案1】:

如果您将单个值发送到服务器进行验证,那么您的操作方法应该只接受一个标量(单值)参数,而不是一个集合。然后您的 URL 将如下所示(假设 {controller}/{action}/{id} 的默认路由表:

/Validation/IsImeiAvailable?ImeiNumber=123456789012345

相应的操作方法签名可能如下所示:

/* note that the param name has to matchthe prop name being validated */
public ActionResult IsImeiAvailable(int ImeiNumber)

编辑: 然后您可以使用它来查找该特定 ID 是否可用。

如果要更改参数的名称,可以修改路由表,但那是另一个话题。

长话短说,如果您想要验证ImeiGadget集合,您需要获取或发布该完整集合。对于单个值,发送或期望整个集合没有多大意义。

更新: 根据新信息,我将查看远程验证属性的放置位置。 听起来它可能被放置在类似IEnumerable<IMEiGadgets> 的东西上,像这样:

[Remote("IsImeiAvailable", "Validation", "'ImeiNumber' is invalid"]
public IEnumerable<ImeiGadget> ImeiGadgets { get; set;}

是否可以移动该属性并将其修改为在 ImeiGadget 类上,而不是像这样?

[Remote("IsImeiAvailable", "Validation", "'ImeiNumber is invalid"]
public int ImeiNumber { get; set;}

理论上,如果您还进行了我上面回答中建议的更改,则无需更改 HTML 模板或脚本上的任何内容即可使其正常工作。理论上。

【讨论】:

  • 将是 IsImeiAvailable(int id) 使用默认路由
  • 顺便说一句,我的假设是所有 OP 需要的是 int ID,从/Validation/IsImeiAvailable?ImeiGadgets[1].ImeiNumber=123456789012345 判断@
  • 请记住我正在使用 MVC3 的远程验证功能。这意味着输入和验证的 ID 符合一定的命名规则:
  • @Josh:我不打算验证整个集合,但控件的 html 呈现为 id="ImeiGadgets_0__ImeiNumber" 导致远程验证调用 /Validation/IsImeiAvailable?ImeiGadgets [1].ImeiNumber=123456789012345。我不能在方法签名中有句点,所以为了获得正确的绑定,我必须绑定到一个集合。
  • @Josh:验证属性已经在 ImeiNumber 属性上。
【解决方案2】:

如果您要发布整个集合,但索引不连续,则可以考虑改为绑定到字典 http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

如果您只发布单个项目或使用 GET 链接,那么您应该修改为

/Validation/IsImeiAvailable?ImeiNumber=123456789012345

public JsonResult IsImeiAvailable(string imeiNumber)

【讨论】:

  • 有什么理由让imeiNumber 绑定到string 而不是int
  • 不确定它是否总是一个 int / long。取决于数据库设置,但我的直觉是它就像一个电话号码(更多的是一个参考号码,你不会计算它)
  • 是有道理的,只是取决于您是需要自己将字符串解析为数字类型还是让模型绑定器来做,或者OP打算进行查找。
  • @Kaido 我无法控制 GET 链接的形成方式。
  • 自定义模型绑定器是我想说的方式
【解决方案3】:

您可以编写自定义模型绑定器:

public class ImeiNumberModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var modelName = bindingContext.ModelName;
        var request = controllerContext.HttpContext.Request;
        var paramName = request
            .Params
            .Keys
            .Cast<string>()
            .FirstOrDefault(
                x => x.EndsWith(modelName, StringComparison.OrdinalIgnoreCase)
            );

        if (!string.IsNullOrEmpty(paramName))
        {
            return bindingContext
                .ValueProvider
                .GetValue(request[paramName])
                .AttemptedValue;
        }

        return null;
    }
}

然后将其应用于控制器动作:

public ActionResult IsImeiAvailable(
    [ModelBinder(typeof(ImeiNumberModelBinder))] string imeiNumber
)
{
    return Json(!string.IsNullOrEmpty(imeiNumber), JsonRequestBehavior.AllowGet);
}

现在ImeiGadgets[xxx] 部分将从查询字符串中被忽略。

【讨论】:

  • 这似乎是最相关的答案。我上班的时候会试试这个
【解决方案4】:

除非您在很多地方需要此绑定功能并且您控制IsImeiAvailable 验证方法然后我认为创建一个自定义模型绑定器开销

你为什么不试试这样一个简单的解决方案,

// need little optimization?
public JsonResult IsImeiAvailable(string imeiNumber)
{
  var qParam = Request.QueryString.Keys
     .Cast<string>().FirstOrDefault(a => a.EndsWith("ImeiNumber"));

  return Json(!string.IsNullOrEmpty(imeiNumber ?? Request.QueryString[qParam]), JsonRequestBehavior.AllowGet);
}

【讨论】:

  • Mark - 目前,验证请求的 URL 类似于“/Validation/IsImeiAvailable?ImeiGadgets[1].ImeiNumber=123456789012345”。因此,您建议的方法不会被称为签名不同。为了使您的建议生效,我需要验证 URL 看起来像“/Validation/IsImeiAvailable?ImeiNumber=123456789012345”
  • 另外,我已经有了一个快速修复,只解析 URL:“string imeiNumber = Request.Url.AbsoluteUri.Substring(Request.Url.AbsoluteUri.IndexOf("=") +1);"
  • 我已经测试了您当前拥有的 URL 的代码并且它有效。在单一简单特殊场景的情况下,快速修复很好。
【解决方案5】:

您可以添加一个带有 .Index 后缀的额外隐藏字段以允许任意索引。

查看:

<form method="post" action="/Home/Create">
    <input type="hidden" name="products.Index" value="cold" />
    <input type="text" name="products[cold].Name" value="Beer" />
    <input type="text" name="products[cold].Price" value="7.32" />

    <input type="hidden" name="products.Index" value="123" />
    <input type="text" name="products[123].Name" value="Chips" />
    <input type="text" name="products[123].Price" value="2.23" />

    <input type="hidden" name="products.Index" value="caliente" />
    <input type="text" name="products[caliente].Name" value="Salsa" />
    <input type="text" name="products[caliente].Price" value="1.23" />

    <input type="submit" />
</form>

型号:

public class Product{
    public string Name{get; set;}
    public decimal Price{get; set;}
}

控制器:

public ActionResult Create(Product[] Products)
{
    //do something..
}

更多信息请参考:You've Been Haacked: Model Bindig To A List

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-13
    相关资源
    最近更新 更多