添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
发怒的红薯  ·  Mysql中case ...·  5 月前    · 
会开车的罐头  ·  bootstrap-datetimepick ...·  1 年前    · 
不开心的开心果  ·  如何通过HTTP ...·  1 年前    · 

.net模型校验和自定义校验

先学习回顾一下模型校验。

模型校验的结果关键词( 模型状态 重新运行验证 特性 验证 内置特性

1、模型状态

模型状态主要分为:模型绑定和模型验证。

模型绑定 的错误通常是数据转换错误。 例如,在一个整数字段中输入一个“x”。

模型验证 在模型绑定后发生,并报数据不符合业务逻辑的错误。 例如,在需要 1 到 5 之间评分的字段中输入 0或者6。

模型绑定和模型验证都在执行控制器操作或 Razor Pages 处理程序方法之前进行。 Web 应用负责检查 ModelState.IsValid 并做出相应响应。

Web 应用通常会重新显示包含错误消息的页面,如以下 controller 示例所示:

public async Task<IActionResult> Create(Movie movie)
    if (!ModelState.IsValid)
        return View(movie);
    _context.Movies.Add(movie);
    await _context.SaveChangesAsync();
    return RedirectToAction(nameof(Index));

如果 Web API 控制器集成或者间接继承 [ApiController] ,则它们不用写检查 ModelState.IsValid 。会自动校验。

在此情况下,如果 模型状态无效,将返回包含错误详细信息的自动 HTTP 400 响应。

特性路由要求

[ApiController] 属性使属性路由成为要求。 例如:

C#复制

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

不能通过由 UseEndpoints UseMvc UseMvcWithDefaultRoute 定义的 传统路由 访问操作。

自动 HTTP 400 响应

[ApiController] 属性使模型验证错误自动触发 HTTP 400 响应。 因此,操作方法中不需要以下代码:

C#复制

if (!ModelState.IsValid)
    return BadRequest(ModelState);

ASP.NET Core MVC 使用 ModelStateInvalidFilter 操作筛选器来执行上述检查。

2、重新运行验证,手动二次校验

有时候数据发生变化,需要手动进行重复验证。 例如,你可能为属性计算一个值,并且希望将属性设置为新值后,再重新运行验证。 若要重新运行验证,请调用 ModelStateDictionary.ClearValidationState 来清除特定于模型的验证,然后再调用 TryValidateModel 对模型进行验证:

C#复制

public async Task<IActionResult> OnPostTryValidateAsync()
    var modifiedReleaseDate = DateTime.Now.Date;
    Movie.ReleaseDate = modifiedReleaseDate;
    ModelState.ClearValidationState(nameof(Movie));
    if (!TryValidateModel(Movie, nameof(Movie)))
        return Page();
    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();
    return RedirectToPage("./Index");

3、 添加特性验证(就是头上写个【xxx】就叫特性,java习惯叫注解)

通过 添加特性验证可以为模型的属性指定验证规则。 以下示例显示使用验证特性进行注释的模型类。 [ClassicMovie] 特性为自定义的验证特性,其他特性为内置的验证特性。

public class Movie
    public int Id { get; set; }
    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;
    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }
    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;
    [Range(0, 999.99)]
    public decimal Price { get; set; }
    public Genre Genre { get; set; }
    public bool Preorder { get; set; }

其中的=null!
现有项目禁用Null状态分析和变量注释,这意味着所有引用类型都可以为Null。从.NET6开始,默认情况下会为新项目启用它们,出发点是为了降低在线上出现抛出System.NullReferenceException的可能性。加上=null!,告诉编译器不用检查了,我这里校验过了。

4、内置特性

以下是一些内置验证特性:

  • [ValidateNever] :指示应将某一属性或参数排除在验证外。
  • [CreditCard] :验证属性是否有信用卡格式。 需要 jQuery Validation 附加方法
  • [Compare] :验证模型中的两个属性是否匹配。
  • [EmailAddress] :验证属性是否有电子邮件格式。
  • [Phone] :验证属性是否有电话号码格式。
  • [Range] :验证属性值是否在指定范围内。
  • [RegularExpression] :验证属性值是否与指定的正则表达式匹配。
  • [Required] :验证字段是否不为 null。 请参阅 s="nolink">[Required] 属性 ,获取关于该特性的行为的详细信息。
  • [StringLength] :验证字符串属性值是否未超过指定长度限制。
  • [Url] :验证属性是否有 URL 格式。
  • [Remote] :通过调用服务器上的操作方法,验证客户端上的输入。 请参阅 ass="nolink">[Remote] 属性 ,获取关于该特性的行为的详细信息。

System.ComponentModel.DataAnnotations 命名空间中可找到验证特性的完整列表。

5、自定义设置错误消息

通过验证特性可以指定要为无效输入显示的错误消息。 例如:

C#复制

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

在内部,特性使用用于字段名的某个占位符调用 String.Format ,有时还使用额外占位符。 例如:

C#复制

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

应用于 Name 属性时,上述代码创建的错误消息将为“名称长度必须介于 6 到 8 之间”。

若要查找为特定特性的错误消息而传递给 String.Format 的参数,请参阅 DataAnnotations 源代码

6、在验证模型错误中使用 JSON

builder.Services.AddControllers(options =>
    options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider());

也可以使用以下代码将验证配置为使用 NewtonsoftJsonValidationMetadataProvider 以在使用 http://Json.NET 时使用 JSON 属性名

builder.Services.AddControllers(options =>
    options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonValidationMetadataProvider());
}).AddNewtonsoftJson();

8、不能为 null 的引用类型和 [Required] 特性

验证系统将不能是 null 的参数或绑定属性视为它们具有 [Required(AllowEmptyStrings = true)] 特性。(.net6自动启用了) 通过an class="nolink">启用 Nullable 上下文,MVC 将隐式开始对不可为 null 的属性或参数进行验证,就像它们已使用 [Required(AllowEmptyStrings = false)] 特性进行了特性化一样。 比如下列代码:

虽然什么都没写,但是请求不写这个参数,就会报错。

public class Person
    public string Name { get; set; }

如果应用是使用 <Nullable>enable</Nullable> 构建的,则 JSON 或表单发布中缺少 Name 的值会导致验证错误。 使用可为 null 的引用类型来实现为 Name 属性指定 NULL 或缺少的值:

public class Person
    public string? Name { get; set; }

如果不希望每个地方都设置引用类型的不为空的话

可以通过在 Program.cs 中配置 SuppressImplicitRequiredAttributeForNonNullableReferenceTypes 来禁用此行为:

builder.Services.AddControllers(
    options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

或者项目.csporj文件中Nullable 配置,找到<Nullable>enable</Nullable>去掉。

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable><!--去掉Nullable开启的配置-->
  </PropertyGroup>

7、[必需] 服务器上校验

在服务器上,如果模型的属性为 null,则认为缺少了所需值。 不能为 null 的字段始终有效,并且也不会显示 [Required] 属性的错误消息。这点就很蛋疼。经常404,但是就是查不出问题?

不能为 null 的属性的模型绑定可能会失败,从而导致 The value '' is invalid 等错误消息。 若要为不可为 null 的类型的服务器端验证指定自定义错误消息,可使用以下选项:

  • 将字段设置为可以为 null(例如, decimal? 而不是 decimal )。 Nullable<T> 值类型被视为标准的可以为 null 的类型。
  • 指定模型绑定要使用的默认错误消息,如以下示例所示:
builder.Services.AddRazorPages()     .AddMvcOptions(options =>     {         options.MaxModelValidationErrors = 50;         options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(             _ => "The field is required.");     });  builder.Services.AddSingleton     <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>(); 
有关模型绑定错误(可以为其设置默认消息)的更多信息,请参阅 DefaultModelBindingMessageProvider。

8、[远程] 特性,自己写一个验证方法,用来验证前台输入

[Remote] 特性用来实现客户端验证,这种验证方式需要在服务器上调用方法,以确定字段输入是否有效。

例如,验证用户名是否已在使用。

若要实现远程验证:

  1. 创建可供 JavaScript 调用的操作方法。 jQuery Validation remote 方法接受 JSON 响应:
  • true 表示输入数据有效。
  • false undefined null 表示输入无效。 显示默认错误消息。
  • 任何其他字符串都表示输入无效。 将字符串显示为自定义错误消息。
以下是返回自定义错误消息的操作方法示例:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyEmail(string email)
    if (!_userService.VerifyEmail(email))
        return Json($"Email {email} is already in use.");
    return Json(true);
在模型类中,使用指向验证操作方法的 [Remote] 特性注释属性,如下例所示:
C#复制
[Remote(action: "VerifyEmail", controller: "Users")] public string Email { get; set; } = null!; 

为已禁用 JavaScript 的客户端实现 服务器端验证

例如验证姓名是否存在,使用附加字段AdditionalFields

通过 [Remote] 特性的 AdditionalFields 属性可以根据服务器上的数据验证字段组合。 例如,如果 User 模型具有 FirstName LastName 属性,可能需要验证该名称对尚未被现有用户占用。 下面的示例演示如何使用 AdditionalFields :,在验证firstName的时候把lastName的值一并带进去验证。

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;

AdditionalFields 可以显式设置为字符串 "FirstName" 和 "LastName",但使用 nameof 运算符可简化稍后的重构过程。 此验证的操作方法必须接受 firstName lastName 参数:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
    if (!_userService.VerifyName(firstName, lastName))
        return Json($"A user named {firstName} {lastName} already exists.");