我有一个表单,用于提交两个对象中的一个。使用Fluent Validation,每个对象都有单独的验证规则,这些规则通过在各自的后处理程序中执行ModelState.Clear((和TryValidateModel(objectName(来处理。验证工作正常,但仅显示在asp验证摘要标记上,而不显示在每个字段附带的标记的asp验证上。有人知道如何绕过这个吗?(使用2个表单不是一种选择。(以下是您可以用来复制问题的代码:
TestValidation.chtml:
@page
@model MyApp.Namespace.TestValidationModel
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>TestValidation</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<h1>Test Validation</h1>
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">ObjectToValidate1 - posts with empty handler, validates server side.</div>
<div class="card-body">
<div class="form-group">
<label class="control-label">Borrower Type</label>
<select asp-for="ObjectToValidate1.Item" asp-items="@Model.DDLItems" class="form-control" onchange="changeBorrowerType();"></select>
<span asp-validation-for="ObjectToValidate1.Item" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label"></label>
<label asp-for="ObjectToValidate1.RequiredString" class="control-label businessName"></label>
<input asp-for="ObjectToValidate1.RequiredString" class="form-control autofocus" />
<span asp-validation-for="ObjectToValidate1.RequiredString" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="control-label"></label>
<input asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="form-control" />
<span asp-validation-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success" asp-page-handler="">Submit</button>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">ObjectToValidate2 - posts with handler of TestValidation, validates serverside</div>
<div class="card-body">
@*<div class="form-group">
<label class="control-label">Borrower Type</label>
<select asp-for="ObjectToValidate2.Item" asp-items="@Model.DDLItems" class="form-control" onchange="changeBorrowerType();"></select>
<span asp-validation-for="ObjectToValidate2.Item" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label"></label>
<label asp-for="ObjectToValidate2.RequiredString" class="control-label businessName"></label>
<input asp-for="ObjectToValidate2.RequiredString" class="form-control autofocus" />
<span asp-validation-for="ObjectToValidate2.RequiredString" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ObjectToValidate2.RequiredStringIfItem1Selected" class="control-label"></label>
<input asp-for="ObjectToValidate2.RequiredStringIfItem1Selected" class="form-control" />
<span asp-validation-for="ObjectToValidate2.RequiredStringIfItem1Selected" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success" asp-page-handler="TestValidation">Submit</button>
</div>*@
</div>
</div>
</div>
</div>
</form>
@*<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>*@
</body>
</html>
测试验证.cshtml.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace MyApp.Namespace
{
public class TestValidationModel : PageModel
{
[BindProperty]
public InputValues ObjectToValidate1 { get; set; }
[BindProperty]
public InputValues ObjectToValidate2 { get; set; }
public List<SelectListItem> DDLItems { get; set; }
public void OnGet()
{
ObjectToValidate1 = new InputValues();
ObjectToValidate2 = new InputValues();
InitializeDDLItems();
}
public ActionResult OnPost()
{
ModelState.Clear();
TryValidateModel(ObjectToValidate1);
if (ModelState.IsValid)
{
// Refresh current page
return RedirectToPage("./TestValidation");
}
InitializeDDLItems();
return Page();
}
public ActionResult OnPostTestValidation()
{
ModelState.Clear();
TryValidateModel(ObjectToValidate2);
if (ModelState.IsValid)
{
// Refresh current page
return RedirectToPage("./TestValidation");
}
InitializeDDLItems();
return Page();
}
private void InitializeDDLItems()
{
DDLItems = new List<SelectListItem>
{
new SelectListItem("-- Select an Item -- ", ""),
new SelectListItem("Item 1", "1"),
new SelectListItem("Item 2", "2"),
};
}
}
public class InputValues
{
[Display(Name ="Item")]
public int Item { get; set; }
[Display(Name = "Required String")]
public string RequiredString { get; set; }
[Display(Name = "Conditional Required String")]
public string RequiredStringIfItem1Selected { get; set; }
}
public class Validator : AbstractValidator<InputValues>
{
public Validator()
{
RuleFor(i => i.Item).NotEmpty().WithMessage("Item is required");
RuleFor(i => i.RequiredString).NotEmpty().WithMessage("Required String is required");
When(i => i.Item.Equals(1), () =>
{
RuleFor(e => e.RequiredStringIfItem1Selected).NotEmpty().WithMessage("Conditional Required String is required if Item 1 selected");
});
}
}
}
您需要添加以下支持客户端验证的javascript文件:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryvalidate/1.17.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>
jQuery Unobtrusive Validation脚本是一个自定义的Microsoft前端库,构建在流行的jQuery Validate插件上。如果没有jQuery Unobtrusive Validation,您将不得不在两个地方编写相同的验证逻辑:一次是在模型属性的服务器端验证属性中,然后是在客户端脚本中。
参考:https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-3.0#客户端验证
答案:不要使用ModelState.Clear((。这样做会清除ModelState,即使在运行TryValidateModel(modelname(之后,生成的ModelState也没有足够的空间来渲染验证标记。因此,这里是我之前的示例的简化版本,ModelState.Clear((被注释掉,ModelState.ClearValidationState((为我们想要验证服务器端的每个对象运行。为了在上面运行我的原始代码,我会为与当前OnPost方法无关的字段清除ValidationState。
剃刀页面:
@page
@model MyApp.Namespace.TestValidation2Model
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>TestValidation</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<h1>Test Validation</h1>
<form method="post">
@*<div asp-validation-summary="All" class="text-danger"></div>*@
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">ObjectToValidate1 - posts with empty handler, validates server side.</div>
<div class="card-body">
<div class="form-group">
<label class="control-label">Borrower Type</label>
<select asp-for="ObjectToValidate1.Item" asp-items="@Model.DDLItems" class="form-control" onchange="changeBorrowerType();"></select>
<span asp-validation-for="ObjectToValidate1.Item" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label"></label>
<label asp-for="ObjectToValidate1.RequiredString" class="control-label businessName"></label>
<input asp-for="ObjectToValidate1.RequiredString" class="form-control autofocus" />
<span asp-validation-for="ObjectToValidate1.RequiredString" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="control-label"></label>
<input asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="form-control" />
<span asp-validation-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success" asp-page-handler="">Submit</button>
</div>
</div>
</div>
</div>
</div>
</form>
@*<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>*@
</body>
</html>
页面模型:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace MyApp.Namespace
{
public class TestValidation2Model : PageModel
{
[BindProperty]
public InputValues2 ObjectToValidate1 { get; set; }
public List<SelectListItem> DDLItems { get; set; }
public void OnGet()
{
ObjectToValidate1 = new InputValues2();
InitializeDDLItems();
}
public ActionResult OnPost()
{
ModelState.ClearValidationState(nameof(ObjectToValidate1.RequiredString));//.Clear();
ModelState.ClearValidationState(nameof(ObjectToValidate1.Item));//.Clear();
ModelState.ClearValidationState(nameof(ObjectToValidate1.RequiredStringIfItem1Selected));
//ModelState.Clear();
TryValidateModel(ObjectToValidate1);
var jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(ModelState);
if (ModelState.IsValid)
{
// Refresh current page
return RedirectToPage("./TestValidation2");
}
InitializeDDLItems();
return Page();
}
private void InitializeDDLItems()
{
DDLItems = new List<SelectListItem>
{
new SelectListItem("-- Select an Item -- ", ""),
new SelectListItem("Item 1", "1"),
new SelectListItem("Item 2", "2"),
};
}
}
public class InputValues2
{
[Display(Name = "Item")]
public int Item { get; set; }
[Display(Name = "Required String")]
public string RequiredString { get; set; }
[Display(Name = "Conditional Required String")]
public string RequiredStringIfItem1Selected { get; set; }
}
public class Validator2 : AbstractValidator<InputValues2>
{
public Validator2()
{
RuleFor(i => i.Item).NotEmpty().WithMessage("Item is required");
RuleFor(i => i.RequiredString).NotEmpty().WithMessage("Required String is required");
When(i => i.Item.Equals(1), () =>
{
RuleFor(e => e.RequiredStringIfItem1Selected).NotEmpty().WithMessage("Conditional Required String is required if Item 1 selected");
});
}
}
}