发布集合和 ModelState

2024-01-04

我的 MVC 应用程序中有一个问题,我不确定如何解决,或者我是否以错误的方式解决它。

我有一个控制器/视图,它在带有复选框的网格中显示项目列表,当这些项目发布到我的控制器时,我想根据传入的 id 从数据库中删除行。

该视图看起来像这样:

@for(int index = 0; index < Model.Items.Length; index++)
{
    <td>
        @Html.HiddenFor(m => m[index].Id)
        @Html.CheckBoxFor(m => m[index].Delete)
    </td>
}

我的控制器接受以下值:

[HttpPost]
public ActionResult Delete(DeleteItemsModel model)
{
    if( !ModelState.IsValid )
    {
        // ...
    }

    foreach( var id in model.Items.Where(i => i.Delete))
        repo.Delete(id);
}

这个场景效果很好。项目已正确发布,并带有 ID 和是否删除的标志,并且已正确删除。我遇到的问题是当我的页面验证失败时。我需要再次从数据库获取项目并将数据发送回视图:

if( !ModelState.IsValid )
{
    var order = repo.GetOrder(id);

    // map
    return View(Mapper.Map<Order, OrderModel>(order));
}

在用户获得要删除的项目列表和单击“提交”之间的时间里,可能会添加新项目。现在,当我提取数据并将其发送回视图时,列表中可能会有新项目。

问题示例:
我在页面上执行 HTTP GET,网格中有两个项目,ID 分别为 2 和 1。我选择第一行(ID 2,按最新排序),然后单击“提交”。页面上的验证失败,我将视图返回给用户。网格中现在有三行 (3, 2, 1)。 MVC 将在第一个项目上有复选框(现在 id 为 3)。如果用户不检查这些数据,那么他们可能会删除错误的内容。

关于如何解决这种情况的任何想法或者我应该做什么? 有谁知道如何


有趣的问题。让我们首先用一个简单的例子来说明问题,因为从其他答案来看,我不确定每个人都明白这里的问题是什么。

假设有以下模型:

public class MyViewModel
{
    public int Id { get; set; }
    public bool Delete { get; set; }
}

以下控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        // Initially we have 2 items in the database
        var model = new[]
        {
            new MyViewModel { Id = 2 },
            new MyViewModel { Id = 1 }
        };
        return View(model);
    }

    [HttpDelete]
    public ActionResult Index(MyViewModel[] model)
    {
        // simulate a validation error
        ModelState.AddModelError("", "some error occured");

        if (!ModelState.IsValid)
        {
            // We refetch the items from the database except that
            // a new item was added in the beginning by some other user
            // in between
            var newModel = new[]
            {
                new MyViewModel { Id = 3 },
                new MyViewModel { Id = 2 },
                new MyViewModel { Id = 1 }
            };

            return View(newModel);
        }

        // TODO: here we do the actual delete

        return RedirectToAction("Index");
    }
}

和一个视图:

@model MyViewModel[]

@Html.ValidationSummary()

@using (Html.BeginForm())
{
    @Html.HttpMethodOverride(HttpVerbs.Delete)
    for (int i = 0; i < Model.Length; i++)
    {
        <div>
            @Html.HiddenFor(m => m[i].Id)
            @Html.CheckBoxFor(m => m[i].Delete)
            @Model[i].Id
        </div>
    }
    <button type="submit">Delete</button>
}

将会发生以下情况:

用户导航至Index操作,选择要删除的第一个项目,然后单击“删除”按钮。以下是他提交表单之前的视图:

调用删除操作,并且当再次呈现视图时(因为存在一些验证错误),用户会看到以下内容:

看看如何预选错误的项目?

为什么会发生这种情况?因为 HTML 帮助程序在绑定时优先使用 ModelState 值而不是模型值,这是设计使然。

那么如何解决这个问题呢?阅读 Phil Haack 的以下博客文章:http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

在他的博客文章中,他谈到非顺序索引并给出以下示例:

<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>

看看我们如何不再对输入按钮的名称使用增量索引?

我们如何将其应用到我们的示例中?

像这样:

@model MyViewModel[]
@Html.ValidationSummary()
@using (Html.BeginForm())
{
    @Html.HttpMethodOverride(HttpVerbs.Delete)
    for (int i = 0; i < Model.Length; i++)
    {
        <div>
            @Html.Hidden("index", Model[i].Id)
            @Html.Hidden("[" + Model[i].Id + "].Id", Model[i].Id)
            @Html.CheckBox("[" + Model[i].Id + "].Delete", Model[i].Delete)
            @Model[i].Id
        </div>
    }
    <button type="submit">Delete</button>
}

现在问题已经解决了。或者是吗?您是否看到了现在所呈现的可怕的混乱景象?我们已经解决了一个问题,但我们在视图中引入了一些绝对令人厌恶的东西。我不知道你怎么想,但当我看到这个的时候我想吐。

那么可以做什么呢?我们应该阅读 Steven Sanderson 的博文:http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/他在其中提出了一个非常有趣的习俗Html.BeginCollectionItem助手的使用方式如下:

<div class="editorRow">
    <% using(Html.BeginCollectionItem("gifts")) { %>
        Item: <%= Html.TextBoxFor(x => x.Name) %>
        Value: $<%= Html.TextBoxFor(x => x.Price, new { size = 4 }) %>
    <% } %>
</div>

注意表单元素是如何包装在这个助手中的吗?

这个助手是做什么的?它用 Guid 替换了强类型助手生成的顺序索引,并使用额外的隐藏字段在每次迭代时设置此索引。


话虽如此,仅当您需要在删除操作中从数据库获取新数据时,才会出现问题。如果您依靠模型绑定器进行再水合,则根本不会有任何问题(除非存在模型错误,您将显示带有旧数据的视图 - >这可能根本不是那么有问题):

[HttpDelete]
public ActionResult Index(MyViewModel[] model)
{
    // simulate a validation error
    ModelState.AddModelError("", "some error occured");

    if (!ModelState.IsValid)
    {

        return View(model);
    }

    // TODO: here we do the actual delete

    return RedirectToAction("Index");
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

发布集合和 ModelState 的相关文章

随机推荐

  • 调用系统调用时的进程状态?

    当调用系统调用时 进程状态如何 我的意思是 不要假设它是像读或写这样的 I O 系统调用 是进程本身执行内核代码 还是进程被挂起并且有一个 内核线程 执行系统调用处理程序 并且知道哪个进程称为 当前 我不确定是否从执行变为就绪 或从执行变为
  • 使用 Go 编程语言的正则表达式查找命名捕获组

    我正在寻找一个正则表达式来查找 其他 正则表达式字符串中的命名捕获组 示例 我想找到 P
  • 如何在更改其内容后使用 Jquery UI 调整 div 的大小?

    我使用 Jquery UI 来允许用户调整带有视频播放器的 div 的大小 这是代码的摘录 div div 当页面呈现时 这将按预期工作 问题是当我触发一个事件来更改 stream div 的内容时
  • UIWebView 不显示我的网页

    对于一些背景信息 我尝试显示的网页是当前托管在 AWS 的 EC2 上的 Web 应用程序 后端是 Python w Flask 前端只是简单的 HTML CSS 该 URL 具有 HTTP 因为它尚未使用 HTTPS 进行保护 当打开该网
  • Spring MVC如何禁止数据绑定到ModelAttribute?

    我有一个简单的 Controller用户登录后呈现页面的类 Controller SessionAttributes user public class DashBoardController RequestMapping value us
  • 在 equals 检查中避免 NullPointerException 的干净方法

    我有一个地址对象 我想为其创建 equals 方法 我可以通过执行类似以下操作 稍微缩短一点 来使这变得非常简单 public boolean equals Object obj if this obj return true if obj
  • NSManagedObject的hasChanges为true,而changedValues为空

    我正在尝试观察个人NSManagedObject变化于NSManagedObjectContextWillSaveNotification void managedObjectContextWillSave NSNotification n
  • 在 VS 2015 和 VS 2013 上运行同一段代码时出现问题

    在 Visual Studio 2013 和 2015 中运行一段代码时 我得到两种不同的结果 在 Visual Studio 2015 上 我得到一个 NullReference 在 2013 年 它按应有的方式工作 Visual Stu
  • 将 JSON NSData 转换为 NSDictionary

    我正在使用 Web 服务的 API 服务 在他们的描述中写道 他们发送 JSON 数据 在我看来 这些数据也与我从中得到的响应相匹配 这是我从 NSURLConnection Delegate connection didReceiveDa
  • 如何创建类的ArrayList?

    如何将一堆类添加到ArrayList
  • 仅使用静态多态性的异构容器

    我的目标是实现一个容器 这里是一组堆栈 每种类型一个 它同时接受许多不同类型的对象 使用 void 指针 或所有存储类型的公共基类 和运行时类型标识 RTTI 在运行时执行此操作非常简单 由于容器将保存的所有类型在编译时都是已知的 因此可能
  • 如何使用 MailApp 在电子邮件中包含内嵌图像

    我有一个简单的 MailApp 来发送 HTML 格式的文本 我的小问题是 如何在该文本中插入内嵌图像 例如 我想为荷兰语文本添加荷兰国旗 为法语内容添加法国国旗 我认为只使用 HTML 代码就可以完成这项工作 但可惜的是 没有这样的运气
  • 更改引荐来源网址的值[重复]

    这个问题在这里已经有答案了 可能的重复 如何在 Javascript 中手动设置 REFERER 标头 https stackoverflow com questions 9580575 how to manually set refere
  • 像工具一样的行为:自定义视图中的文本?

    我有一个自定义视图 布局中有两个文本视图 我们来叫一个key和另一个value 所以你知道怎么做TextView有这个吗
  • 如何从 Minecraft 编辑 .mca 文件?

    介绍 我想将我的各个 Minecraft 世界合并成一个世界 这似乎是一个相对容易的壮举 但随着我的研究 它演变成需要制作一个自定义程序 奋斗 我首先移动区域文件并将它们合并到一个区域文件夹中 这似乎是显而易见的解决方案 而且几乎有效 注意
  • 如何在Plone 5上自定义folder_contents?

    我在 Plone 5 上 我想修改默认显示的列数 folder contents 如果我没记错的话 整个table是一种在相应标签上使用相当大的 JSON data xxx 参数进行初始化的模式 那么 我应该深入研究 JSON 还是将注意力
  • 使用 jQuery 绘制动画线条

    我想使用 jQuery 来创建就像用隐形笔绘制线条图的效果 有点像这样 http d2fhka9tf2vaj2 cloudfront net tuts 152 QTiPad Milestones JavaScriptWebsite html
  • 如何修复 facebook Messenger-checkbox 的 CSP 问题

    我正在努力使Facebook Messenger 复选框 https developers facebook com docs messenger platform plugin reference checkbox plugin工作 我已
  • SKLabelNode没有透明背景

    您好 我在 SKLabelNode 上使用自定义字体 我可以设置字体颜色 但是还可以 但是文本的内部部分是透明的 例如 有什么方法可以将这种颜色设置为白色吗 到目前为止我的代码 scoreLabel fontColor SKColor co
  • 发布集合和 ModelState

    我的 MVC 应用程序中有一个问题 我不确定如何解决 或者我是否以错误的方式解决它 我有一个控制器 视图 它在带有复选框的网格中显示项目列表 当这些项目发布到我的控制器时 我想根据传入的 id 从数据库中删除行 该视图看起来像这样 for