【问题标题】:How to properly preserve view rendering data on model validation failure?如何在模型验证失败时正确保留视图渲染数据?
【发布时间】:2017-05-02 12:16:20
【问题描述】:

在我的应用程序中,我有一个表单,用户可以在其中将银行账户添加到他们的账户中。我通过控制器上的两个操作实现了这一点,即:

public async Task<IActionResult> Add()
{
    var client = await _clientFactory.CreateClientAsync();

    // We obtain the available account types from a remote service.
    var accountTypes = await client.GetAccountTypesAsync();

    var viewModel = new AddAccountViewModel
    {
        AccountTypes = accountTypes;
    }

    return View(viewModel);
}

[HttpPost]
public async Task<IActionResult> Add(AddAccountViewModel model)
{
    if (!ModelState.IsValid)
        return View(model);

    // Store the account for the user, and redirect to a result page.
}

我的AddAccountModel目前看起来是这样的,有两个目的:

public class AddAccountViewModel
{
    public List<AccountTypeModel> AccountTypes { get; set; }

    [Required]
    [StringLength(100)]
    public string AccountName { get; set; }

    [Required]
    [Range(1, int.MaxValue]
    public decimal CurrentBalance { get; set; }

    [Required]
    public Guid AccountTypeId { get; set; }
}

一方面,AccountTypes 属性用于填充视图中的列表:

@{
    var accountTypeList = new SelectList(accountTypes, "Id", "Name");
}

<select class="form-control" id="accountType" asp-for="AccountTypeId" asp-items="@accountTypeList">

</select>

当我通过GET /Add 到达初始页面时,一切正常。表单呈现,选择框填充了服务中的项目,正如我所期望的那样。

但是,表单还包括验证,如果用户输入无效数据或将字段留空,POST /Add 会被调用,通过模型,运行验证,最终将用户重定向回GET /Add,具体验证失败的原因。这就是问题出现的地方。

当用户被重定向回表单并被要求更正他们的输入时,我的模型中的AccountTypesnull。这是有道理的,因为我们没有将它作为POST 请求的一部分发送——它仅作为表单呈现的输入。由于表单不知道帐户类型,用户将永远无法更正他们的输入,因为他们将无法选择有效的帐户类型。

如上所述,我的模型是双重的:一方面它包含视图需要呈现的数据,另一方面它包含用户提供的数据,然后由应用程序处理。

鉴于上述情况,我的问题是:当模型验证失败时,我应该如何正确保存视图呈现所需的输入数据?即,我的ViewModel 是保存AccountTypeModels 集合的正确位置,还是应该将它们保存在ViewData 或什至TempData

目前,我的应用程序和服务都是无状态的,这就是为什么我还没有走“把它放在 TempData 中”的路线,因为据我所知,TempData 存储在服务器的会话中。如果可以避免的话,我也不想在视图开始时查询可用帐户类型的服务,因为它似乎违反了 MVC 的关注点分离。

感谢阅读,希望有人能帮我解决这个问题。

【问题讨论】:

  • 您只需在 POST 方法中返回视图之前重新填充 AccountTypes。但是你的视图模型最好包含IEnumerable&lt;SelectListItem&gt; AccountTypes,而不是在视图中生成SelectList
  • 我不知道我怎么没想到。我已将视图模型更改为包含 IEnumeable&lt;SelectListItem&gt; 并将视图直接绑定到该模型,并添加了代码以重新获取 POST 中的可用帐户类型。如果您发表评论作为答案,我会很乐意接受。

标签: c# asp.net-mvc asp.net-core


【解决方案1】:

在返回视图之前,您需要在 POST 方法中重新填充 AccountTypes 集合。我建议您的视图模型包含属性public IEnumerable&lt;SelectListItem&gt; AccountTypes { get; set; } 而不是List&lt;AccountTypeModel&gt;,这样您就不需要在视图中生成SelectList

为了保持 DRY,创建一个私有方法来配置你的视图模型

public async Task<IActionResult> Add()
{
    var model = new AddAccountViewModel();
    ConfigureViewModel(model);
    return View(model);
}

[HttpPost]
public async Task<IActionResult> Add(AddAccountViewModel model)
{
    if (!ModelState.IsValid)
    {
        ConfigureViewModel(model);
        return View(model);
    }
    ....
}

private async Task ConfigureViewModel(AddAccountViewModel model)
{
    var client = await _clientFactory.CreateClientAsync();
    var accountTypes = await client.GetAccountTypesAsync();
    model.AccountTypes = new SelectList(accountTypes, "Id", "Name");
}

【讨论】:

    猜你喜欢
    • 2016-05-08
    • 2015-05-31
    • 1970-01-01
    • 1970-01-01
    • 2020-02-23
    • 2023-02-10
    • 2013-06-30
    • 1970-01-01
    • 2013-07-25
    相关资源
    最近更新 更多