如何在 Razor 页面中实现两个具有单独 BindProperties 的表单?
Posted
技术标签:
【中文标题】如何在 Razor 页面中实现两个具有单独 BindProperties 的表单?【英文标题】:How to implement two forms with separate BindProperties in Razor Pages? 【发布时间】:2018-07-09 01:13:18 【问题描述】:我正在使用带有 Razor 页面的 ASP.NET Core 2,并且我试图在一个页面上拥有两个具有不同属性 (BindProperty) 的表单。
@page
@model mfa.Web.Pages.TwoFormsModel
@
Layout = null;
<form method="post">
<input asp-for="ProductName" />
<span asp-validation-for="ProductName" class="text-danger"></span>
<button type="submit" asp-page-handler="Product">Save product</button>
</form>
<form method="post">
<input asp-for="MakerName" />
<span asp-validation-for="MakerName" class="text-danger"></span>
<button type="submit" asp-page-handler="Maker">Save maker</button>
</form>
以及对应的PageModel:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
namespace mfa.Web.Pages
public class TwoFormsModel : PageModel
[BindProperty]
[Required]
public string ProductName get; set;
[BindProperty]
[Required]
public string MakerName get; set;
public async Task<IActionResult> OnPostProductAsync()
if (!ModelState.IsValid)
return Page();
return Page();
public async Task<IActionResult> OnPostMakerAsync()
if (!ModelState.IsValid)
return Page();
return Page();
点击两个提交按钮中的任何一个都会让我进入相应的帖子处理程序。 “ProdutName”和“MakerName”都正确地填充了我在相应输入字段中键入的任何内容。到目前为止,一切顺利。
但是:ModelState.IsValid() 总是返回真——不管相应属性的值是否有值。即使两个属性都为空,ModelState.IsValid() 也为真。
另外:OnPostProductAsync() 应该只验证“ProductName”,因此 OnPostMakerAsync() 应该只验证“MakerName”。
这可以做到吗?还是我对 Razor Pages 的要求太多了?有很多博客和教程向您展示如何在一个页面上拥有两个表单……但它们都使用相同的模型。我需要不同的模型!
【问题讨论】:
您可以根据表单中的 asp-page-handler 属性非常简单地执行此操作。您可以在此处查看示例:youtube.com/watch?v=-6PE4p4gUYQ 2021,我们仍然没有很好的方法来做到这一点! WTF 微软?每页1个表格真的吗?如果我想在一页中有用户设置和应用程序设置的表单怎么办 - 我不能。我必须创建一些模型怪物,将所有值保留在那里等等.. 我已经打开了一个与此github.com/dotnet/aspnetcore/issues/37606相关的问题 【参考方案1】:为了使验证正常工作,您必须创建一个包含这两个属性的视图模型,并为您要检查的每个属性定义 [必需],但因为您有两个不同的表单验证它不会起作用,因为如果两个值都按要求定义,那么当您尝试验证产品时,它也会验证没有值的制造商。
您可以做的是自己进行检查。例如 OnPostProduct 可以有以下代码:
public async Task<IActionResult> OnPostProductAsync()
if (string.IsNullOrEmpty(ProductName))
ModelState.AddModelError("ProductName", "This field is a required field.");
return Page();
// if you reach this point this means that you have data in ProductName in order to continue
return Page();
【讨论】:
@pitardis:感谢您的帮助。是的,您的代码确实有效。不过,我希望有一个更“优雅”的解决方案。当然,在我的实际应用程序中,视图模型比只有一两个字符串变量要复杂一些。因此,手动检查所有验证规则似乎是一项乏味的工作。但你知道吗:如果没有人想出更好的主意,我肯定会接受你的建议。 据我所知,没有其他方法可以做到这一点。我已经为几家酒店做过很多次了,当你说这“看起来像是一项乏味的工作”时,我知道你的意思。 谢谢!好吧,让我们看看其他人是否有另一个绝妙的主意:) Allrigthy,目前没有更好的解决方案。我走的是石头路……谢谢pitaridis!【参考方案2】:我的解决方案不是很优雅,但它不需要您手动进行验证。您可以保留[Required]
注释。
您的 PageModel
将如下所示 -
private void ClearFieldErrors(Func<string, bool> predicate)
foreach (var field in ModelState)
if (field.Value.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
if (predicate(field.Key))
field.Value.ValidationState = Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Valid;
public async Task<IActionResult> OnPostProductAsync()
ClearFieldErrors(key => key.Contains("MakerName"));
if (!ModelState.IsValid)
return Page();
return Page();
public async Task<IActionResult> OnPostMakerAsync()
ClearFieldErrors(key => key.Contains("ProductName"));
if (!ModelState.IsValid)
return Page();
return Page();
不是最好的主意,因为您需要将绑定的字段名称与字符串进行比较。我使用了Contains
,因为字段键不一致,有时包含前缀。对我来说已经足够好了,因为我的表单很小,字段名称不同。
【讨论】:
【参考方案3】:另一个解决方案非常接近......
public static class ModelStateExtensions
public static ModelStateDictionary MarkAllFieldsAsSkipped(this ModelStateDictionary modelState)
foreach(var state in modelState.Select(x => x.Value))
state.Errors.Clear();
state.ValidationState = ModelValidationState.Skipped;
return modelState;
public class IndexModel : PageModel
public class Form1Model
// ...
public class Form2Model
// ...
[BindProperty]
public Form1Model Form1 get; set;
[BindProperty]
public Form2Model Form2 get; set;
public async Task<IActionResult> OnPostAsync()
ModelState.MarkAllFieldsAsSkipped();
if (!TryValidateModel(Form1, nameof(Form1)))
return Page();
// ...
【讨论】:
感谢您提供此解决方案。我使用了 ModelState.Clear();它工作正常。 谢谢 - 保留属性允许 javascript 验证也可以工作。【参考方案4】:我遇到了同样的问题,这就是我的解决方案。
public class IndexModel : PageModel
private readonly ILogger<IndexModel> _logger;
[BindProperty]
public IndexSubscribeInputModel SubscribeInput get; set;
[BindProperty]
public IndexContactInputModel ContactInput get; set;
public IndexModel(ILogger<IndexModel> logger)
_logger = logger;
public void OnGet()
SubscribeInput = new IndexSubscribeInputModel();
ContactInput = new IndexContactInputModel();
public void OnPostSubscribe()
if (IsValid(SubscribeInput))
return;
public void OnPostContact()
if (IsValid(ContactInput))
return;
public class IndexSubscribeInputModel
[Required(AllowEmptyStrings =false, ErrorMessage ="0 é obrigatório!")]
public string Email get; set;
public class IndexContactInputModel
[Required(AllowEmptyStrings = false, ErrorMessage = "0 é obrigatório!")]
public string Email get; set;
[Required(AllowEmptyStrings = false, ErrorMessage = "0 é obrigatório!")]
public string Message get; set;
private bool IsValid<T>(T inputModel)
var property = this.GetType().GetProperties().Where(x => x.PropertyType == inputModel.GetType()).FirstOrDefault();
var hasErros = ModelState.Values
.Where(value => value.GetType().GetProperty("Key").GetValue(value).ToString().Contains(property.Name))
.Any(value =>value.Errors.Any());
return !hasErros;
我可能会将“IsValid”方法放在 PageModelExtensions 类中,这样会更花哨。
希望这对某人有所帮助...
【讨论】:
感谢您的回答,这对我很有帮助,我编辑了您的帖子以提供更易读的IsValid
版本。以上是关于如何在 Razor 页面中实现两个具有单独 BindProperties 的表单?的主要内容,如果未能解决你的问题,请参考以下文章