使用RedirectToAction跳转后嵌套ViewModel数据丢失问题
我被这个问题卡了好久,之前类似操作都正常,现在找不到问题出在哪,求帮忙!
我在页面加载3个ViewModel,通过BeginForm提交字段到控制器方法,对部分字段用三元表达式处理后,用RedirectToAction跳转到确认页面(Verification),想展示用户刚提交的内容。
Submit方法里ViewModel的字段数据都正常,但跳转后两个嵌套ViewModel的数据没了。Post方法里能看到完整数据,也把模型传给了RedirectToAction,但进入Verification视图后数据就丢了?
视图模型
public class MainViewModel { public Guid Id { get; set; } public string? Name { get; set; } public string? Email {get; set;} public TypesViewModel TypeViewModel {get; set;} } public class TypesViewModel { public Guid Id { get; set; } public Guid ExternalId {get; set;} public bool Type {get; set;} public string? Name { get; set; } public CustomFieldsViewModel CustomFieldViewModel {get; set;} } public class CustomFieldsViewModel { public bool Email { get; set; } public bool Phone {get; set;} public bool Text {get; set;} public string? EmailContact { get; set; } public string? PhoneNumberContact {get; set;} public string? TextMessageContact {get; set;} public String? PhoneNumber {get; set;} }
控制器代码
public class PrimaryController : Controller { public IActionResult Index() { MainViewModel mainViewModel = new MainViewModel(); TypesViewModel typesViewModel = new TypesViewModel(); mainViewModel.TypeViewModel = typesViewModel; CustomFieldsViewModel customFieldsViewModel = new CustomFieldsViewModel(); mainViewModel.TypeViewModel.CustomFieldViewModel = customFieldsViewModel; return View(mainViewModel); } [HttpPost] public ActionResult Submit(MainViewModel model) { model.Id = Guid.NewGuid(); model.TypeViewModel.Id = Guid.NewGuid(); model.TypeViewModel.ExternalId = Guid.NewGuid(); model.TypeViewModel.Name = model.TypeViewModel.Type == true ? "Yes, Contact me." : "No, Do not contact me."; model.TypeViewModel.CustomFieldViewModel.EmailContact = model.TypeViewModel.CustomFieldViewModel.Email == true ? "Contact by Email" : "Do Not Contact By Email"; model.TypeViewModel.CustomFieldViewModel.PhoneNumberContact = model.TypeViewModel.CustomFieldViewModel.Phone == true ? "Contact by Phone Number" : "Do Not Contact By Phone Number"; model.TypeViewModel.CustomFieldViewModel.TextMessageContact = model.TypeViewModel.CustomFieldViewModel.Text == true ? "Contact by Text Message" : "Do Not Contact By Text Message"; return RedirectToAction("Verification", model); } public ActionResult Verification(MainViewModel model) { return View(model); } }
视图代码
Index视图
@model MainViewModel @{ ViewData["Title"] = "Primary Output"; } <div class="container d-flex align-items-center justify-content-center"> <div class="box"> @using (Html.BeginForm("Submit", "Primary", FormMethod.Post)) { <label>Name</label> @Html.TextBoxFor(m => m.Name, new { @class = "form-control"}) <label>Email Address</label> @Html.TextBoxFor(m => m.Email, new { @class = "form-control", onblur="checkEmail(this.value)", Type = "email"}) <label>Can we contact you?</label> @Html.CheckBoxFor(m => m.TypeViewModel.Type) <span>Yes</span> <div class="contact-options"> <label>Prefered form of communication?</label> <ul class="list"> <li> <input type="checkbox" value="true" name="Model.TypeViewModel.CustomFieldViewModel.Email" id="Email"> <span>Email</span> </li> <li> <input type="checkbox" value="true" name="Model.TypeViewModel.CustomFieldViewModel.Phone" onclick="ShowHideDiv(this)" id="Phone"> <span>Phone</span> </li> <li> <input type="checkbox" value="true" name="Model.TypeViewModel.CustomFieldViewModel.Text" onclick="ShowHideDiv(this)" id="Text"> <span>Text</span> </li> </ul> </div> <div id="Phone-Number"> <label>Phone Number</label> <input class="form-control" type="tel" name="Model.TypeViewModel.CustomFieldViewModel.PhoneNumber" /> </div> <div class="button-container"> <input id="Submit" type="Submit" value="Submit" /> </div> } </div> </div>
Verification视图
@model MainViewModel @{ ViewData["Title"] = "Verification"; } <div class="container d-flex align-items-center justify-content-center"> <div class="circular-square"> <img src="https://i.ibb.co/YpsHjmW/The-Big-Lebowski-hp-GQ-25-Feb16-rex-b.jpg" border="0"> </div> </div> <div class="verification-container d-flex align-items-center justify-content-center"> <div class="verification-title">The Dude Abides.</div> </div> <div class="verification-container d-flex align-items-center justify-content-center"> <div class="box"> <p>Congrats! You've made it to the final round. Below is the information you submitted!</p> <p><label>Id:</label> @Model.Id</p> <p><label>Name:</label> @Model.Name</p> <p><label>Email:</label> @Model.Email</p> <p><label>Type Id:</label> @Model.TypeViewModel.Id</p> <p><label>Type Name:</label> @Model.TypeViewModel.Name</p> <p><label>Custom Field 1:</label> @Model.TypeViewModel.CustomFieldViewModel.PhoneNumberContact</p> <p><label>Custom Field 2:</label> @Model.TypeViewModel.CustomFieldViewModel.EmailContact</p> <p><label>Custom Field 3:</label> @Model.TypeViewModel.CustomFieldViewModel.TextMessageContact</p> @if(@Model.TypeViewModel.CustomFieldViewModel.PhoneNumber != null) { <p><label>Phone Number:</label> @Model.TypeViewModel.CustomFieldViewModel.PhoneNumber</p> } <p><label>External Id:</label> @Model.TypeViewModel.ExternalId</p> </div> </div>
问题原因及解决办法
核心问题1:RedirectToAction的模型传递机制
RedirectToAction是重定向操作,会发起新的GET请求。ASP.NET Core仅会将模型的顶级属性转换为URL查询参数,嵌套的ViewModel(如TypeViewModel、CustomFieldViewModel)无法被正确序列化,导致Verification方法接收到的嵌套模型为空。
核心问题2:视图表单字段命名错误
手动编写的input标签name属性带了Model.前缀(如Model.TypeViewModel.CustomFieldViewModel.Email),但正确的绑定命名应该去掉Model.,否则提交时嵌套字段无法正确绑定到MainViewModel。
解决步骤:
修复视图表单字段命名
改用Html辅助方法生成字段,避免手动命名错误:<li> @Html.CheckBoxFor(m => m.TypeViewModel.CustomFieldViewModel.Email) <span>Email</span> </li> <li> @Html.CheckBoxFor(m => m.TypeViewModel.CustomFieldViewModel.Phone, new { onclick="ShowHideDiv(this)" }) <span>Phone</span> </li> <li> @Html.CheckBoxFor(m => m.TypeViewModel.CustomFieldViewModel.Text, new { onclick="ShowHideDiv(this)" }) <span>Text</span> </li>电话号码输入框修改为:
@Html.TextBoxFor(m => m.TypeViewModel.CustomFieldViewModel.PhoneNumber, new { @class = "form-control", type = "tel" })用TempData传递复杂模型
因为RedirectToAction无法传递嵌套模型,改用TempData存储序列化后的模型:
修改Submit方法:[HttpPost] public ActionResult Submit(MainViewModel model) { // 原有处理逻辑不变 model.Id = Guid.NewGuid(); model.TypeViewModel.Id = Guid.NewGuid(); model.TypeViewModel.ExternalId = Guid.NewGuid(); model.TypeViewModel.Name = model.TypeViewModel.Type ? "Yes, Contact me." : "No, Do not contact me."; model.TypeViewModel.CustomFieldViewModel.EmailContact = model.TypeViewModel.CustomFieldViewModel.Email ? "Contact by Email" : "Do Not Contact By Email"; model.TypeViewModel.CustomFieldViewModel.PhoneNumberContact = model.TypeViewModel.CustomFieldViewModel.Phone ? "Contact by Phone Number" : "Do Not Contact By Phone Number"; model.TypeViewModel.CustomFieldViewModel.TextMessageContact = model.TypeViewModel.CustomFieldViewModel.Text ? "Contact by Text Message" : "Do Not Contact By Text Message"; // 序列化模型存入TempData TempData["MainModel"] = JsonSerializer.Serialize(model); return RedirectToAction("Verification"); }修改Verification方法:
public ActionResult Verification() { if (TempData["MainModel"] is string json) { var model = JsonSerializer.Deserialize<MainViewModel>(json); return View(model); } // 无数据时跳转回首页 return RedirectToAction("Index"); }添加无参构造函数
给每个ViewModel显式添加无参构造函数,避免后续模型绑定失败:public class MainViewModel { public MainViewModel() {} // 原有属性 } public class TypesViewModel { public TypesViewModel() {} // 原有属性 } public class CustomFieldsViewModel { public CustomFieldsViewModel() {} // 原有属性 }
内容的提问来源于stack exchange,提问作者IAMABANANA

