.NET Core 6动态表单添加前后端验证方案咨询
动态表单的前后端必填验证实现(.NET Core 6)
问题背景
我在.NET Core 6 Web应用中有一个基于项数组动态生成输入字段的表单,每个项定义了字段类型、数据类型以及是否为必填项。目前表单生成逻辑正常,但需要给必填项(IsMandatory=true)添加客户端+服务端验证,且要能通过ModelState.IsValid校验。由于字段是动态的,无法用常规ViewModel特性标记。当前已引入aspnet-validation.js,想确认是否还需额外JS实现客户端验证。
现有代码
CSHTML代码(精简版)
@using Microsoft.AspNetCore.Mvc.TagHelpers @inherits UmbracoViewPage<AdditionalInfoViewModel> <fieldset class="panel--shadow"> <div class="panel theme--white"> @for (var i = 0; i < Model.AdditionalInfo.Count; i++) { var item = Model.AdditionalInfo[i]; var required = item.IsMandatory ? "required" : ""; var fieldId = item.UdfName.Replace(' ', '-'); <div class="form-group mb-4 @required"> <input asp-for="@Model.AdditionalInfo[i].UdfName" type="hidden" /> @{ var fieldName = Model.AdditionalInfo[i].TitleOverride; switch (Model.AdditionalInfo[i].UdfDataType) { case "Text": <label for="@fieldId" class="form-label mb-2">@fieldName</label> <input class="form-control" id="@fieldId" asp-for="@Model.AdditionalInfo[i].UdfValue" type="text"/> break; default: <label for="@fieldId" class="form-label mb-2">@fieldName</label> <input class="form-control d-inline-block w-auto" id="@fieldId" asp-for="@Model.AdditionalInfo[i].UdfValue" type="text"/> break; } if (Model.AdditionalInfo[i].IsMandatory) { <span class="field-validation-valid" data-valmsg-for="@fieldId" data-valmsg-replace="true"></span> } } </div> } </div> <div class="panel theme--river text-center"> <button class="btn btn-theme btn-primary js-additionalinfo-continue" type="@Html.If(Model.HasNextStep, "button", "submit")"> @if (!Model.HasNextStep) { <i class="far fa-shopping-basket" aria-hidden="true"></i> } @Html.If(Model.HasNextStep, "Continue", "Add to basket") <i class="fa-regular fa-arrow-right" aria-hidden="true"></i> </button> </div> </fieldset>
ViewModel定义
public class AdditionalInfoViewModel { public AdditionalInfoViewModel() { Notice = string.Empty; AdditionalInfo = new List<AdditionalInfoListItem>(); } public string Notice { get; set; } public bool HasNextStep { get; set; } public List<AdditionalInfoListItem>? AdditionalInfo { get; set; } }
AdditionalInfoListItem定义
public class AdditionalInfoListItem { public string UdfName { get; set; } public string? TitleOverride { get; set; } public bool IsMandatory { get; set; } public string? UdfDataType { get; set;} public List<ListValueItem>? ListValues { get; set; } public string? UdfValue { get; set; } } public class ListValueItem { public string ListValueName { get; set; } public bool Selected { get; set; } }
解决方案
一、服务端验证实现
由于字段是动态生成的,无法使用DataAnnotation特性,需在Action中手动校验必填项:
[HttpPost] public IActionResult Submit(AdditionalInfoViewModel model) { if (model.AdditionalInfo != null) { foreach (var (item, index) in model.AdditionalInfo.Select((item, idx) => (item, idx))) { if (item.IsMandatory && string.IsNullOrWhiteSpace(item.UdfValue)) { // 生成与前端name属性一致的字段名,确保错误信息关联到对应输入框 var fieldName = $"AdditionalInfo[{index}].UdfValue"; var displayName = item.TitleOverride ?? item.UdfName; ModelState.AddModelError(fieldName, $"{displayName} 为必填项"); } } } if (!ModelState.IsValid) { // 返回原视图并显示验证错误 return View(model); } // 后续业务逻辑处理 return RedirectToAction("Success"); }
二、客户端验证实现
你已引入aspnet-validation.js,无需额外JS库,但需修改CSHTML代码生成正确的验证标记:
修改后的CSHTML核心代码
@for (var i = 0; i < Model.AdditionalInfo.Count; i++) { var item = Model.AdditionalInfo[i]; var required = item.IsMandatory ? "required" : ""; var fieldId = item.UdfName.Replace(' ', '-'); var displayName = item.TitleOverride ?? item.UdfName; <div class="form-group mb-4 @required"> <input asp-for="@Model.AdditionalInfo[i].UdfName" type="hidden" /> @{ switch (Model.AdditionalInfo[i].UdfDataType) { case "Text": <label for="@fieldId" class="form-label mb-2"> @displayName @(item.IsMandatory ? "<span class='text-danger'>*</span>" : "") </label> <input class="form-control" id="@fieldId" asp-for="@Model.AdditionalInfo[i].UdfValue" type="text" @(item.IsMandatory ? $"data-val='true' data-val-required='{displayName} 为必填项'" : "")/> break; default: <label for="@fieldId" class="form-label mb-2"> @displayName @(item.IsMandatory ? "<span class='text-danger'>*</span>" : "") </label> <input class="form-control d-inline-block w-auto" id="@fieldId" asp-for="@Model.AdditionalInfo[i].UdfValue" type="text" @(item.IsMandatory ? $"data-val='true' data-val-required='{displayName} 为必填项'" : "")/> break; } // 使用asp-validation-for自动关联验证提示,替代手动写的span if (item.IsMandatory) { <span asp-validation-for="@Model.AdditionalInfo[i].UdfValue" class="text-danger"></span> } } </div> }
关键调整点
- 给必填项手动添加
data-val="true"和data-val-required属性,这是ASP.NET Core客户端验证的识别标记。 - 用
asp-validation-for标签助手替代手动编写的验证提示容器,它会自动生成与输入框name匹配的data-valmsg-for属性,确保错误信息正确显示。 - 给必填项标签添加红色星号,增强用户体验。
验证初始化(可选)
如果布局页未引入_ValidationScriptsPartial.cshtml,需手动初始化验证:
<script> document.addEventListener('DOMContentLoaded', function() { const forms = document.getElementsByTagName('form'); Array.from(forms).forEach(form => aspnetValidation.validate(form)); }); </script>
验证效果
- 服务端:提交后Action的手动校验会将错误加入
ModelState,ModelState.IsValid返回false,错误信息自动回显到对应输入框下方。 - 客户端:
aspnet-validation.js会在表单提交前检查必填项,阻止非法提交并显示错误提示,无需额外自定义JS。
内容的提问来源于stack exchange,提问作者GordonS
相关产品推荐
相关产品推荐

