2

我正在使用 ASP.NET MVC3,并且我有一个具有多个属性的视图模型,其中一些用于向用户显示,其中一些用作用户的输入并且可能具有默认值。我对 GET 请求(唯一的参数是获取内容的标识符)和帖子使用相同的视图模型,它们将整个视图模型作为操作中的参数。我的控制器使用通过从 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();
4

2 回答 2

2

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

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

于 2012-09-11T20:54:52.450 回答
0

我不熟悉 Spark 视图引擎,但您的表单似乎只显示信息并具有隐藏字段?

但是,我相信您真正想要实现的是允许用户编辑 UserInput3 和 UserInput4。因此,我重写了您的控制器并查看我认为您试图实现的目标,并包含包含我的答案的评论。该视图是使用 razor 视图引擎编写的,因此您必须转移到 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" />
}
于 2012-09-13T21:12:16.737 回答