ASP.NET Core 2 Razor Pages:OnPost中Page()方法作用及报错排查
嘿,我来帮你把这个问题掰明白——你遇到的情况其实是Razor Pages里Page()方法的常见误区,咱们一步步拆解:
Page()到底干啥的 Page()方法的核心作用就是返回当前页面的视图,并且把当前的PageModel实例传给视图渲染。但它有个很重要的特点:它不会自动帮你做两件事——
- 不会自动执行
OnGet里的逻辑(比如你从数据库拉取下拉列表数据的操作) - 不会自动把表单提交的旧数据填充到
PageModel的属性里
简单说,你调用Page()的时候,视图只会用你当前PageModel里已有的数据来渲染,缺啥就会报错啥。
NullReferenceException? 你在OnGet里给ItemModel赋值了Categories这些下拉列表数据,但OnPost是一个全新的请求,和之前的OnGet完全独立——也就是说,当你在OnPost里验证失败调用Page()时,ItemModel还是null!视图里要访问Model.ItemModel.Categories,自然就空引用报错了。
而且你一开始没处理表单提交的itemModel参数,视图绑定的是Model.ItemModel,所以就算用户填了内容,视图也拿不到旧数据。
你需要在调用Page()之前,手动把所有需要的数据都准备好,具体分三步:
1. 重新加载页面必需的基础数据
首先得重新调用_itemService.GetCreateViewModel(),把下拉列表这类从数据库来的数据重新赋值给ItemModel——不然下拉列表还是空的。
2. 把用户提交的旧数据合并到ItemModel里
你需要把表单提交的itemModel里的用户输入值,同步到ItemModel中。这里推荐用TryUpdateModelAsync,它会自动把请求里的表单值绑定到ItemModel的指定属性上,比手动赋值更靠谱:
await TryUpdateModelAsync(ItemModel, "", i => i.Name, i => i.Description, i => i.ImageUpload); // 列出你需要保留的表单字段
当然你也可以手动赋值,比如ItemModel.Name = itemModel.Name;,但字段多的话就麻烦了。
3. 确保ModelState的错误被保留
ModelState里的验证错误是自动保留的,只要你没手动清除它,asp-validation-for标签就能正常显示错误——前提是你的视图绑定的是Model.ItemModel的属性,比如:
<input asp-for="ItemModel.Name" /> <span asp-validation-for="ItemModel.Name" class="text-danger"></span>
修改后的完整OnPost方法大概是这样:
public async Task<IActionResult> OnPost(ItemCreateViewModel itemModel) { // 第一步:重新加载下拉列表等基础数据 ItemModel = await _itemService.GetCreateViewModel(); // 第二步:把用户提交的旧数据绑定到ItemModel上 if (await TryUpdateModelAsync(ItemModel, "", i => i.Name, i => i.Description, i => i.ImageUpload)) { // 验证模型有效性 if (!ModelState.IsValid) { return Page(); // 这时候ItemModel有基础数据+旧表单值,ModelState有错误,视图能正常渲染 } var item = _itemService.CreateItem(ItemModel); var image = await ImagesController.ProcessFormImage(ItemModel.ImageUpload.UploadPhoto, item.Id, ModelState); if (!ModelState.IsValid) { return Page(); // 同样,这里的ItemModel已经准备好所有数据了 } await _itemService.SaveItemImage(image); _itemService.CreateItem(ItemModel); return RedirectToPage("./Index"); } // 如果模型绑定失败,直接返回页面 return Page(); }
this.ItemModel = itemModel看不到验证错误? 因为你直接把ItemModel替换成了itemModel,而itemModel里没有Categories这些下拉列表数据——页面渲染的时候先因为下拉列表空了报错,掩盖了验证错误。另外,直接替换模型可能导致ModelState和视图绑定的属性不匹配,就算有错误也显示不出来。
内容的提问来源于stack exchange,提问作者Alexander Kozachenko

