ASP.NET 核心:asp-* 属性在模型上使用请求负载?

ASP.NET Core: asp-* attributes use request payload over model?

似乎在ASP.NET Core中,asp-*属性中的值(例如asp-for)是从模型之前的请求负载中获取的。示例:

Post这个值:

MyProperty="User entered value."

此操作:

[HttpPost]
public IActionResult Foo(MyModel m)
{
    m.MyProperty = "Change it to this!";
    return View();
}

或此操作

[HttpPost]
public IActionResult Foo(MyModel m)
{
    m.MyProperty = "Change it to this!";
    return View(m);
}

视图呈现:

<input asp-for="MyProperty" />

表单输入中的值是 User entered value. 而不是 Change it to this!

首先,令我惊讶的是我们不需要将模型传递给视图并且它可以工作。其次,令我震惊的是,请求负载优先于传递到视图中的模型。任何人都知道这个设计决定的基本原理是什么?使用 asp-for 属性时有没有办法覆盖用户输入的值?

我可以证实你的观察。真正让您大吃一惊的是:

[HttpPost]
public IActionResult Foo (MyModel m)
{
    m.MyProperty = "changed";
    var result = new MyModel { MyProperty = "changed" };
    return View(result);
}

...给你同样的结果。

我认为你应该记录一个错误:https://github.com/aspnet/mvc/issues


编辑: 我现在记得我自己以前遇到的这个问题,并承认它不一定是一个错误,而是一个意外的结果。执行此代码的结果的原因并不明显。可能没有一种非常简单的方法来对此发出警告,但 PRG 是一个值得遵循的好模式。

我相信这是预期的behavior/by设计。因为当您提交表单时,表单数据将存储到 ModelState 字典中,当 razor 呈现您的表单元素时,它将使用 Model 状态字典中的值。这就是为什么即使您没有将视图模型的对象传递给 View() 方法,您也会看到表单元素值的原因。

如果要更新输入值,需要明确清除模型状态字典。您可以使用 ModelState.Clear() 方法。

[HttpPost]
public IActionResult Create(YourviewModel model)
{       
    ModelState.Clear();
    model.YourProperty = "New Value";
    return View(model);
}

它使用模型状态字典来呈现表单元素值的原因是为了支持用例,例如在发生验证错误时在表单中显示先前提交的值。

编辑: 我在 aspnet mvc 的官方 github 回购中找到了一个 link,其中 this is confirmed 由 Eilon (asp.net 团队成员)

https://github.com/aspnet/Mvc/issues/4486#issuecomment-210603605