在开发ASP.NET Core MVC应用程序时,需要对控制器中的模型校验数据有效性,元数据注释(Data Annotations)是一个完美的解决方案。

元数据注释最典型例子是确保API的调用者提供了某个属性的值,在传统的ASP.NET MVC中使用的是RequiredAttribute特性类。该属性仍然可以在ASP.NET Core MVC中使用,但也提供了一个新的特性类BindRequiredAttribute

今天让我们来看看它们之间的细微差别。

RequiredAttribute的典型用法

想象一下,您的模型是下面的BookOrder类。为了强制AuthorTitle属性的值始终出现在传入的请求中,并且值不能为空,我们使用RequiredAttribute修饰它们。

    public class BookOrder
{
[Required]
public string Author { get; set; } [Required]
public string Title { get; set; }
}

这在传统的ASP.NET MVC中是很常见方法。有了这个,您可以验证ModelState是否存在错误,因为对这些属性的验证失败会反映在这里。示例:

    [Route("bookorder")]
public IActionResult PostBook([FromForm]BookOrder bookOrder)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
} // 正常逻辑代码
return Content("OK");
}

如果传入的请求中缺少两个必需的属性(或者属性的值为String.Empty),则响应将是一个400状态码,并将模型状态错误序列化到响应中。

使用Postman进行测试,请求中只提供了Title属性的值,而未提供Author属性的值:

常有趣的问题,例如当我们添加类似数量的属性时(类型为int)。在这种情况下,RequiredAttribute将无法正常工作,因为默认值为0,并且该属性不可能为空。即使客户端在不包括属性值的情况下提交请求,模型实例的值都将为0。换句话说,RequiredAttribute不起作用。在我们的例子中,我们使用的是int,但是同样适用于所有值的类型,例如DateTimeGuid

以前,在传统的ASP.NET MVC中,解决这个问题的方法是使用一个可为空类型,如下所示:

    public class BookOrder
{
[Required]
public string Author { get; set; } [Required]
public string Title { get; set; } [Required]
public int? Quantity { get; set; }
}

虽然它解决了这个问题,但是这不是最优雅的方式。Quantity属性不能为null,如果API的调用者未提供该属性的值,则该值保持为null,配合RequiredAttribute特性可以达到我们的目的。

这种方法的缺点是,在正常逻辑代码中,您需要直接访问可为空的Value属性。

    [Route("bookorder")]
public IActionResult PostBook([FromForm]BookOrder bookOrder)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
} // 正常逻辑代码
var requestedQuantity = bookOrder.Quantity.Value; return Content("OK");
}

如果通过静态分析工具来检查代码,因为它们找不不到空值检测,所以会警告您可能出现空引用异常(即使在逻辑上,ModelState检测是足够的)。所以这并不是令人赏心悦目的方案。

如果在VS中安装了Resharper工具,会显示如下警告:

这就是BindRequiredAttribute产生的原因。它和RequiredAttribute的工作方式类似,它要求请求必须提示属性的值 。

我们将模型改为如下结构,Quantity属性为int类型,并使用[BindRequired]修饰:

    public class BookOrder
{
[Required]
public string Author { get; set; } [Required]
public string Title { get; set; } [BindRequired]
public int Quantity { get; set; }
}

使用Postman进行测试,在请求体不包括Quantity属性的值:

测试结果为ModelState.IsValid返回的false,要求提供Quantity属性的值。

RequiredAttribute与BindRequiredAttribute的差别

BindRequiredAttribute特性类只要求API的调用者提供该属性的值,但不进行空值检测,允许提供的值为空,例如我们将模型改为如下示例,所有属性中都使用了BindRequiredAttribute特性类进行注释:

    public class BookOrder
{
[BindRequired]
public string Author { get; set; } [BindRequired]
public string Title { get; set; } [BindRequired]
public int Quantity { get; set; }
}

使用Postman进行测试,在请求体包括这三个属性名称的参数,但是属性的值均为空或默认值:

测试结果为正常,BindRequiredAttribute特性类只要求API的调用者提供该属性的值,但不进行空值检测,允许提供的值为空

RequiredAttribute 与 BindRequiredAttribute 混用问题

当然,在代码中混合使用[Required][BindRequired]并不是一个最令人满意的效果。值得庆幸的是,ASP.NET Core MVC为您提供了足够的灵活性来改变RequiredAttribute的行为,强制它包含BindRequiredAttribute的行为。

您可以通过实现自己的IBindingMetadataProvider并在您的应用程序全局范围内注册来实现这一目标。

    public class RequiredBindingMetadataProvider : IBindingMetadataProvider
{
public void CreateBindingMetadata(BindingMetadataProviderContext context)
{
if (context.PropertyAttributes?.OfType<RequiredAttribute>().Any() ?? false) {
context.BindingMetadata.IsBindingRequired = true;
}
}
}

在全局范围内注册它:

    public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(o =>
{
o.ModelMetadataDetailsProviders.Add(new RequiredBindingMetadataProvider());
});
}

相关补充内容

1、ASP.NET Core MVC还提供了BindNeverAttribute特性类用于指定该属性不进行模型绑定。例如:您拥有一个像IsAdmin这样的属性,这需要通过服务端来指定,而不是通过客户提交的数据来指定;

2、如果客户端提交的是Json格式的数据,BindRequiredAttributeBindNeverAttribute特性类就不会起任何作用,这是因为模型直接通过Json.Net反序列化来创建,MVC框架并不知道属性的值从何而来。

上面补充的两点,感兴趣的同学可以自己进行测试。

总结

  • RequiredAttribute特性类的行为在传统的ASP.NET MVC与ASP.NET Core MVC是一致的,校验属性的值不能为null
  • RequiredAttribute特性类对于值类型的属性,就没有任何效果了,所以在编码的过程将属性的类型改为可为空类型,不过编码的过程就比较纠结了,需要访问可为空类型的Value属性,而且静态代码检测工具会警告需要进行非空的校验;
  • BindRequiredAttribute特性类可以解决值类型默认值的问题,强制要求客户端提交请求时,必须包含属性的值,不过属性的值可以为空,不进行非空的校验;
  • RequiredAttributeBindRequiredAttribute混合使用的情况下,我们也提供了优雅的方法,让RequiredAttribute包含BindRequiredAttribute的行为。

#cnblogs_post_body.cnblogs-markdown p img
{
max-width:95%
}

ASP.NET Core MVC中的 [Required]与[BindRequired]的更多相关文章

  1. 008.Adding a model to an ASP.NET Core MVC app --【在 asp.net core mvc 中添加一个model (模型)】

    Adding a model to an ASP.NET Core MVC app在 asp.net core mvc 中添加一个model (模型)2017-3-30 8 分钟阅读时长 本文内容1. ...

  2. 007.Adding a view to an ASP.NET Core MVC app -- 【在asp.net core mvc中添加视图】

    Adding a view to an ASP.NET Core MVC app 在asp.net core mvc中添加视图 2017-3-4 7 分钟阅读时长 本文内容 1.Changing vi ...

  3. ASP.NET Core MVC 中的 [Controller] 和 [NonController]

    前言 我们知道,在 MVC 应用程序中,有一部分约定的内容.其中关于 Controller 的约定是这样的. 每个 Controller 类的名字以 Controller 结尾,并且放置在 Contr ...

  4. ASP.NET Core MVC 中设置全局异常处理方式

    在asp.net core mvc中,如果有未处理的异常发生后,会返回http500错误,对于最终用户来说,显然不是特别友好.那如何对于这些未处理的异常显示统一的错误提示页面呢? 在asp.net c ...

  5. 006.Adding a controller to a ASP.NET Core MVC app with Visual Studio -- 【在asp.net core mvc 中添加一个控制器】

    Adding a controller to a ASP.NET Core MVC app with Visual Studio 在asp.net core mvc 中添加一个控制器 2017-2-2 ...

  6. ASP.NET Core MVC中构建Web API

    在ASP.NET CORE MVC中,Web API是其中一个功能子集,可以直接使用MVC的特性及路由等功能. 在成功构建 ASP.NET CORE MVC项目之后,选中解决方案,先填加一个API的文 ...

  7. 在ASP.NET Core MVC中子类Controller拦截器要先于父类Controller拦截器执行

    我们知道在ASP.NET Core MVC中Controller上的Filter拦截器是有执行顺序的,那么如果我们在有继承关系的两个Controller类上,声明同一种类型的Filter拦截器,那么是 ...

  8. ASP.NET MVC和ASP.NET Core MVC中获取当前URL/Controller/Action (转载)

    ASP.NET MVC 一.获取URL(ASP.NET通用): [1]获取完整url(协议名+域名+虚拟目录名+文件名+参数) string url=Request.Url.ToString(); [ ...

  9. ASP.NET Core MVC中URL和数据模型的匹配

    Http GET方法 首先我们来看看GET方法的Http请求,URL参数和ASP.NET Core MVC中Controller的Action方法参数匹配情况. 我定义一个UserController ...

随机推荐

  1. 微信小程序爬坑日记

    新公司上手小程序.30天,从入门到现在,还没放弃... 虽然小程序发布出来快一年了,爬坑的兄弟们大多把坑都踩平了.而我一直停留在"Hello World"的学习阶段.一来没项目,只 ...

  2. 分布式版本控制系统 Git 教程

    简介 Git 是什么? Git 是一个开源的分布式版本控制系统. 什么是版本控制? 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统. 什么是分布式版本控制系统? 介绍分布 ...

  3. 洛谷教主花园dp

    洛谷-教主的花园-动态规划   题目描述 教主有着一个环形的花园,他想在花园周围均匀地种上n棵树,但是教主花园的土壤很特别,每个位置适合种的树都不一样,一些树可能会因为不适合这个位置的土壤而损失观赏价 ...

  4. Nginx动静分离实现

    Nginx动静分离实现: Nginx是一种轻量级,高性能,多进程的Web服务器,非常适合作为静态资源的服务器使用,而动态的访问操作可以使用稳定的Apache.Tomcat及IIS等来实现,这里就以Ng ...

  5. Laravel 框架安装

    Composer 在命令行执行 create-project 来安装 Laravel:就是下面这一句话就OK了!composer create-project laravel/laravel --pr ...

  6. tp中like多字段同时怎么模糊搜索

    例如 select * from tbl where a like '%123%' or b like '%123%' or c like '%123%' ;实现这样的功能,thinkphp怎么写呢? ...

  7. 掌握NIO,程序人生

    就像新IO为java带来的革新那样,让我们也开启一段新的程序人生. 关键字:NIO,BIO,伪IO,AIO,多路复用选择器,通道,缓冲区,jdk研究,回调函数,高并发 java.nio 概述 历史背景 ...

  8. Angular页面加载闪现解决方案 ng-cloak

    在做Angular项目时,经常会遇见在浏览器上闪烁表达式({{ express }} ),及模块(div)的闪烁,会闪现/闪烁隐藏的数据,之前用过vue.js,可以通过v-clock解决,同理Angu ...

  9. 让你的Javascript提升70%性能

    现在的JavaScript代码要进行性能优化,通常使用一些常规手段,如:延迟执行.预处理.setTimeout等异步方式避免处理主线程,高大上一点的会使用WebWorker.即使对于WebWorker ...

  10. 大数据:Hadoop入门

    大数据:Hadoop入门 一:什么是大数据 什么是大数据: (1.)大数据是指在一定时间内无法用常规软件对其内容进行抓取,管理和处理的数据集合,简而言之就是数据量非常大,大到无法用常规工具进行处理,如 ...