ASP.Net MVC 回发和模型

本文关键字:模型 Net MVC ASP | 更新日期: 2023-09-27 18:33:25

这主要是对这个issu中评论的后续,但我没有足够的声誉来评论......

ASP.Net MVC 将标签值回发到控制器

假设我有一个简单的模型:

public class SimpleClass
{
    public String Label { get; set; }
    public String FirstName { get; set; }
}

标签根据用户/客户端进行更改,因此它不能是数据属性。如果在回发处理时出现问题,我们需要重绘整个页面。这是上一篇文章问题的症结所在。可接受的解决方案是这样做:

@Html.DisplayTextFor(model => model.Label)
@Html.HiddenFor(model => model.Label)
@Html.EditorFor(model => model.FirstName)

这是有道理的,因为它有效。但是我们的模型要复杂得多,范围要广泛得多。这种方法将导致大量隐藏字段,这似乎是一个非常肮脏的解决方案。

这让我想到了JP的评论:

ASP.Net MVC 将标签值回发到控制器

解决方案是重新加载模型。但这不仅仅是重新加载,也是合并,因为您希望保留任何客户端数据更改。

default: SimpleClass { Label="TheLabel", FirstName="Rob"}
postedback: SimpleClass { Label="", FirstName="Steve" }
we want: SimpleClass { Label="TheLabel", "FirstName="Steve" }

我的问题是 MVC 是否有一种很好的方法来知道哪些字段被回发以便正确合并?我们只需要合并回发字段而不是空白属性。

还是只 ajaxize 整个回发而不提交表单更好?这样可以避免提交时出现所有模型重新加载问题。

更新

为了给巴勃罗点赞,我接受了他的解决方案。要查看我的解决方案的简单示例,请查看Robert Harvey在下面的答案中的评论:

ASP.Net MVC 回发和模型

ASP.Net MVC 回发和模型

这里的主要问题是试图将WebForms的PostBack概念融入MVC。没有有状态回发这样的东西,其中事物只是自动保留其状态。

您只有绑定到视图的视图模型

,以及由视图发布到控制器的视图模型。它们甚至不一定需要属于同一类型。这意味着,控制器应仅接收用户确实可以更改的数据,而不是具有许多属性的大型对象,这些属性是初始 ViewModel 的一部分,但是是只读的。

标签通常表示只读文本,它们不是可编辑的表单元素。这就是为什么您必须为此使用隐藏字段的原因。

是的,有时这意味着您必须在控制器中重新加载原始数据,并与您发布的新数据同步,这不一定是一件坏事。如果将只读数据绑定到用户无法手动编辑的视图,则不应真正信任之后在帖子中返回的数据。仅仅因为您的 html 可能会尝试将其设为只读并不意味着我无法在您不知情的情况下操纵帖子并最终更改您的"只读"数据。

我刚刚读了你提到的第二个问题,从外观上看,他的主要问题是他试图再次重用同一个 ViewModel,所以所有数据都丢失了,模型无效。解决方案确实非常简单,仅将您需要的内容作为新的 ViewModel 类型发布,并让控制器负责其余的工作。

[移自OP]

我认为这就是巴勃罗对那些想知道的人的建议。这似乎是解决此问题的好模式。

模型:

    public class SimpleClass : SimpleClassPostBack
    {
        public String Label { get; set; }
        public SimpleClass()
        {
            // simulate default loading
            Label = "My Label";
            FirstName = "Rob";
        }
    }
    // contains only editable by the user fields
    public class SimpleClassPostBack
    {
        public String FirstName { get; set; }
    }

控制器操作:

    [HttpGet]
    public ActionResult SimpleClassExample3()
    {
        SimpleClass simpleClass = new SimpleClass();
        return View(simpleClass);
    }
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult SimpleClassExample3(SimpleClassPostBack postBackSimpleClass)
    {
        Boolean errorOccurred = true;
        if (!errorOccurred)
        {
            // do whatever success action is necessary
        }
        // redraw the page, an error occurred
        // reload the original model
        SimpleClass simpleClass = new SimpleClass();
        // move the posted back data into the model
        // can use fancy reflection to automate this
        simpleClass.FirstName = postBackSimpleClass.FirstName;
        // bind the view
        return View(simpleClass);
    }

视图:

@model SimpleClass
@{
    ViewBag.Title = "Simple Class Example3";
}
<h2>Simple Class Example3</h2>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <label for="FirstName">@Html.DisplayFor(m => m.Label)</label>
    @Html.EditorFor(m => m.FirstName)
    <br/>
    <button>Submit</button>
}

您应该只将数据从客户端发送到服务器无法自行"找出"的数据。 如果服务器知道用户首次导航到该视图时的标签是什么,那么如果用户无法修改它们,则服务器将能够在重新加载视图时知道标签是什么。

使用隐藏字段标识数据库对象。因此,您的SimpleClass可能应该具有某种Id,您将在隐藏输入中使用。使用EditorFor进行FirstName。现在,当发布表单时,使用发送Id从数据库中查找正确的SimpleClass,并使用发布的值修改其 FirstName 属性。Label属性将null没关系,因为您不需要保存它。现在,如果帖子中存在问题,并且您希望像以前一样发送相同的视图,则需要以用户首次到达视图时相同的方式重新填充LabelIdFirstName 属性的值将自动发送回具有模型状态的视图。

总结:

  • 仅发布识别某些内容和用户所需的数据可以在该视图中进行编辑。
  • 不要相信客户会向您发送任何有效的东西。用户可以将隐藏字段标签的值更改为任何值。