ASP.NET MVC View-Model:从无效帖子的后端重新加载或将所有属性放入隐藏的输入中

时间:2012-09-11 20:42:40

标签: asp.net-mvc-3 spark-view-engine asp.net-mvc-viewmodel

我正在使用ASP.NET MVC3,我有一个具有多个属性的视图模型,其中一些属性用于向用户显示,其中一些用作用户的输入,可能有默认值值。我对GET请求使用相同的视图模型(唯一的参数是获取内容的标识符)和post,它将整个视图模型作为操作中的参数。我的控制器使用通过从NHibernate会话中提取实体的业务逻辑层检索的实体填充视图模型。

在视图中为所有只读字段放置隐藏输入是否更好?如果在具有无效视图模型的帖子之后呈现页面,它们将存在,或者仅使用输入更好用户真的提供数据并重新加载后端的其余部分并将其合并到?

谢谢!

编辑:

为什么?

编辑:

控制器通常看起来像这样:

public class MyController : BaseController /* BaseController provide BizLogic object */
{
    [HttpGet]
    public ActionResult EditSomething(Int32 id)
    {
        MyDomainObject = base.BizLogic.GetMyDomainObjectById(id);
        MyViewModel model = new MyViewModel();
        model.Id = id;
        model.ReadOnly1 = MyDomainObject.Field1;
        model.Readonly2 = MyDomainObject.Field2;
        model.UserInput3 = MyDomainObject.Field3;
        model.UserInput4 = MyDomainObject.Field4;
        return View(model);
    }

    [HttpPost]
    public ActionResult EditSomethingMyViewModel model)
    {
        PerformComplexValidationNotDoneByAttributes(model);
        if (ModelState.Valid)
        {
            BizLogicSaveTransferObject transferObject =
                new BizLogicSaveTransferObject();
            transferObject.Id = model.Id;
            transferObject.Field3 = model.UserInput3;
            transferObject.Field4 = model.UserInput4;
            base.BizLogic.SaveDomainObject(transferObject);

            return RedirectToAction("EditSomething", new { id = model.Id });
        }
        else
        {
            #if reload_non_input_fields_from_db

            MyDomainObject = base.BizLogic.GetMyDomainObjectById(model.Id);
            model.ReadOnly1 = MyDomainObject.Field1;
            model.Readonly2 = MyDomainObject.Field2;

            #endif

            return View(model);
        }
    }
}

视图看起来像这样:

# Html.BeginForm();
${Html.ValidationSummary()}
    <p>ID: ${Model.Id}</p><input type="hidden" name="${Html.NameFor(m => m.Id)}" value="${Model.Id" />
    <p>Read Only One: ${Model.ReadOnly1}</p><!-- uncomment if not reload_non_input_fields_from_db <input type="hidden" name="${Html.NameFor(m => m.ReadOnly1)}" value="${Model.ReadOnly1}" />-->
    <p>Read Only Two: ${Model.ReadOnly2}</p><!-- uncomment if not reload_non_input_fields_from_db <input type="hidden" name="${Html.NameFor(m => m.ReadOnly2)}" value="${Model.ReadOnly2}" />-->
    <p>Input Three: ${Model.UserInput3}</p><input type="hidden" name="${Html.NameFor(m => m.UserInput3)}" value="${Model.UserInput3}" />
    <p>Input Three: ${Model.UserInput4}</p><input type="hidden" name="${Html.NameFor(m => m.UserInput3)}" value="${Model.UserInput4}" />
# Html.EndForm();

2 个答案:

答案 0 :(得分:2)

如果它们是只读的,那么将任何输入放入页面是没有意义的(当然,除了唯一的记录ID字段外)。在您编写时,合并允许用户修改的字段。

你需要以任何方式合并字段;对于只读字段,永远不应该根据您发送给客户端的数据覆盖这些字段,并假设会回复给您。即使您将输入“隐藏”,它们也不会被隐藏;例如,任何知道如何使用Firebug的人都可以轻松修改它们。

答案 1 :(得分:0)

我不熟悉Spark视图引擎,但看起来你的表单只显示信息并隐藏了字段?

但是,我相信您实际尝试实现的目的是允许用户编辑UserInput3和UserInput4。因此,我重写了你的控制器,并查看我认为你想要实现的内容,并包含了包含我的答案的评论。视图是使用剃刀视图引擎编写的,因此您必须转移到Spark。

我希望这就是你想要的:

<强>控制器:

//HttpGet has been removed as this is implied
public ViewResult EditSomething(Int32 id)
{
    MyDomainObject = base.BizLogic.GetMyDomainObjectById(id);
    MyViewModel model = new MyViewModel()  
    {
        //this uses C# 3.0 object initializer syntax
        model.Id = MyDomainObject.Id,
        model.ReadOnly1 = MyDomainObject.Field1,
        model.Readonly2 = MyDomainObject.Field2,
        model.UserInput3 = MyDomainObject.Field3,
        model.UserInput4 = MyDomainObject.Field4
    };
    return View(model);
}

//It is common (although not required) for the Post action to have the same name as the Get action
[HttpPost]
public ActionResult EditSomething (MyViewModel model)
{
    //I recommend you take a look at FluentValidation to validate your models.
    //Hopefully, you should no longer have the limitations of the data attributes and you will not require PerformComplexValidationNotDoneByAttributes()
    //This will allow you to just simply check the model state.
    if (ModelState.IsValid)
    {
        BizLogicSaveTransferObject transferObject = new BizLogicSaveTransferObject()
        {
            //You should write the new user inputs to the database.
            transferObject.Id = model.Id,
            transferObject.Field3 = model.UserInput3,
            transferObject.Field4 = model.UserInput4
        };
        base.BizLogic.SaveDomainObject(transferObject);

        //You were previously returning a redirect to the same action???  Do you need to pass the id?
        return RedirectToAction("NextAction", new { id = model.Id });
    }

    //Note: The else is not required as a return was used in the previous if.

    //IN ANSWER TO YOUR QUESTION, you should re-retrieve the domain object from the database to get the information to display again
    //as these values will be null/default values in your model.
    //There will be a performance cost in retrieving this information again but you could not rely on this data to be sent from the user.
    //Nor, should it have been included in the request as you should keep the size of the request down as indicated here:
    //http://developer.yahoo.com/performance/rules.html
    MyDomainObject = base.BizLogic.GetMyDomainObjectById(model.Id);
    model.ReadOnly1 = MyDomainObject.Field1;
    model.Readonly2 = MyDomainObject.Field2;

    return View(model);
}

查看:

@model MyViewModel

<!--
    The read only fields which are only used to display information have been included outside of the form.
    As this information is only used for display purposes we do not want to send it back as:
    1. It will increase the size of the request.
    2. We can not rely on it as a mischievous user may have tampered with it.
-->
<p>Read Only One: @Html.TextBoxFor(m => m.ReadOnly1)</p>
<p>Read Only Two: @Html.TextBoxFor(m => m.ReadOnly2)</p>

@using (Html.BeginForm())
{
    <!--
        The id is still passed using a hidden field as you still need to identify the database entity in the post action.
        I have not displayed the id as there seemed little point.
        Again, a user could change this id so you would need to check that the id exists in the database.
    -->
    @Html.HiddenFor(m => m.Id)

    <!--
        The user inputs do not need to have their previous values passed via hidden fields.
        This increases the size of the request and by the looks of it you are not using them in the post controller.
    -->
    <p>@Html.TextBoxFor(m => m.UserInput3)</p>
    <p>@Html.TextBoxFor(m => m.UserInput4)</p>

    <input type="submit" value="submit" />
}