.NET之模型绑定和验证

介绍

模型绑定就是接收将来自HTTP请求的数据映射到模型的过程。如果找不到模型属性的值,并不会报错,而是给该属性设置默认值。

示例:比如我们有一个接口为

[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)

这个时候你的请求为:http://localhost:5000/api/pets/2?DogsOnly=true

路由系统选择该Action后,模型绑定会执行以下的步骤:

  • 查找 GetByID 的第一个参数,该参数是一个名为 id 的整数。

  • 查找 HTTP 请求中的可用源,并在路由数据中查找 id =“2”。

  • 将字符串“2”转换为整数 2。

  • 查找 GetByID 的下一个参数,该参数是一个名为 dogsOnly 的布尔值。

  • 查找源,并在查询字符串中查找“DogsOnly=true”。名称匹配不区分大小写。

  • 将字符串“true”转换为布尔值 true

最后会调用GetById方法,参数Id为2,参数dogsOnly为true。

默认情况下,模型绑定以键值对的形式从HTTP请求中的以下源中获取数据:

  1. 表单域

  2. 请求正文

  3. 路由数据

  4. 查询字符串参数

  5. 上传的文件

对于每个参数,按照顺序扫描源。也可以直接指定源

  • [FromQuery] - 从查询字符串获取值。

  • [FromRoute] - 从路由数据获取值。

  • [FromForm] - 从发布表单字段中获取值。

  • [FromBody] - 从请求正文获取值。

  • [FromHeader] - 从 HTTP 标头获取值。

示例:

[HttpGet]
public async Task<User> GetAsync([FromQuery]string id)[HttpGet]
public async Task<User> GetAsync([FromRoute]string id)[HttpGet]
public async Task<User> GetAsync([FromForm]string id)[HttpPost]
public async Task<ActionResult<string>> AddAsync([FromBody]AddUserVm dto)public void OnGet([FromHeader(Name = "Accept-Language")] string language)

也可以编写自定义的值提供程序,比如从cookie中获取会话状态,参考:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/model-binding?view=aspnetcore-5.0#additional-sources

模型绑定

简单模型绑定

例如:bool、byte、char、DateTime、DateTimeOffset、float、enum、guid、int、TimeSpan、Url、Version等

复杂类型

使用复杂类型必须具有要绑定的公共默认构造函数和公共可写属性。进行模型绑定时候,将使用公共默认构造函数来实例化类。对于复杂类型的每个属性,模型绑定会查找名称模式 prefix.property_name 的源。如果未找到,它将仅查找不含前缀的 properties_name。不过一般我们使用都是进行完全匹配,特殊需求才会做此操作。

参考资料:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/model-binding?view=aspnetcore-5.0#complex-types

内置自定义模型绑定

通过ByteArrayModelBinder 可以实现将传输的base64编码字符串转换为字节数组。

比如:

        [HttpPost]public void Post([FromForm] byte[] file, string filename){var trustedFileName = Path.GetRandomFileName();var filePath = Path.Combine("e://", trustedFileName);if (System.IO.File.Exists(filePath)){return;}System.IO.File.WriteAllBytes(filePath, file);}

请求示例

image.png

接收结果

image.png

自定义模型绑定

示例场景:通过请求头传递后端自定义的一种token,通过自定义模型绑定将token解析后绑定到请求模型。

参考资料:https://www.cnblogs.com/jyzhu/articles/8670536.html

请求接口示例

        [HttpGet]public ActionResult GetToken(TokenModel dto){return Ok(dto);}

首先定义token模型类

    public class TokenModel{public int UserID { get; set; }public string UserName { get; set; }}

自定义模型绑定器

    public class TokenModelBinder : IModelBinder{/// <summary>/// 请求里传递参数token/// </summary>/// <param name="bindingContext"></param>/// <returns></returns>public Task BindModelAsync(ModelBindingContext bindingContext){//参数必须包含tokenif (!(bindingContext.ActionContext.HttpContext.Request.Headers.ContainsKey("token")))return Task.CompletedTask;var token = bindingContext.ActionContext.HttpContext.Request.Headers["token"];//TODO  解析tokenvar result = new TokenModel(){UserID = 111,UserName = "azrng",};bindingContext.Result = ModelBindingResult.Success(result);return Task.CompletedTask;}}

定义token框架绑定器

    public class TokenModelBinderProvider : IModelBinderProvider{public IModelBinder GetBinder(ModelBinderProviderContext context){if (context == null){throw new ArgumentNullException(nameof(context));}if (context.Metadata.ModelType == typeof(TokenModel))return new TokenModelBinder();return null;}}

启用绑定器

    services.AddControllers(options =>{options.ModelBinderProviders.Insert(0, new TokenModelBinderProvider());});

请求示例

var client = new RestClient("http://localhost:5000/api/ModelVerify/GetToken");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("token", "123456");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);

结果就是可以在GetToken方法参数获取到我们token的值。

模型校验

现在dotNetCore如果在控制器标识[ApiController],那么就会在进action前就会自动校验模型类绑定是否符合要求,如果不符合要求自动触发HTTP400错误响应。原文

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

验证特性

通过验证特性可为属性增加验证规则。不仅仅有内置的验证特性,还可以实现自定义验证特性。

内置验证特性

常用的有:必填、长度验证、数值范围、手机号码、邮箱,还可以使用正则验证

    public class AddModelVerify{[Display(Name = "名称"), Required(ErrorMessage = "{0}不能为空")]// 非空校验 [MinLength(6, ErrorMessage = "名称不能小于6位")] // 最小长度校验[MaxLength(10, ErrorMessage = "长度不超过10个")] // 最大长度校验public string UserName { get; set; }/// <summary>/// 密码/// </summary>[Display(Name = "密码"), Required(ErrorMessage = "{0}不能为空")][MinLength(6, ErrorMessage = "密码必须大于6位")]public string PassWord { get; set; }[Display(Name = "工号")] // 友好名称错误提示[Required(ErrorMessage = "{0}不能为空")][StringLength(10, MinimumLength = 1, ErrorMessage = "{0}长度是{1}")]public string EmployeeNo { get; set; }}public IActionResult VerifyPhone([RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)

除了上面这些还有其他内置特性:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-5.0#built-in-attributes

请求地址传入空值,输出结果:HTTP错误400

{"errors": {"PassWord": ["密码不能为空","密码必须大于6位"],"UserName": ["名称不能为空","名称不能小于6位"],"EmployeeNo": ["工号不能为空","工号长度是10"]},"type": "https://tools.ietf.org/html/rfc7231#p-6.5.1","title": "One or more validation errors occurred.","status": 400,"traceId": "00-d16b945b3e172a42bfe5b53d08f7487b-8d87c2ca238fdc4a-00"
}

还有一个Remote特性感觉挺有意思,使用场景是比如在ID上标注远程特性,绑定时候自定验证ID是否有效

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyID(string id)
{if (!_userService.VerifyID(id)){return Json($"对象未找到");}return Json(true);
}

模型类使用指向操作方法的[Remote]特性注释属性

[Remote(action: "VerifyID", controller: "Users")]
public string ID { get; set; }

Remote其他用法:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-5.0#additional-fields

自定义特性

对于内置验证特性无法处理的情况,我们可以创建自定义验证特性。

模拟场景:添加用户时候,设置名字和工号不能一致,出生日期必须小于当前时间

输入模型类

    public class AddUserinfoVm{[Display(Name = "名称"), Required(ErrorMessage = "{0}不能为空")][MinLength(6, ErrorMessage = "名称不能小于6位")][MaxLength(10, ErrorMessage = "长度不超过10个")]public string UserName { get; set; }/// <summary>/// 密码/// </summary>[Display(Name = "密码"), Required(ErrorMessage = "{0}不能为空")][MinLength(6, ErrorMessage = "密码必须大于6位")]public string PassWord { get; set; }[Display(Name = "工号")][Required(ErrorMessage = "{0}不能为空")][StringLength(10, MinimumLength = 1, ErrorMessage = "{0}长度是{1}")]public string EmployeeNo { get; set; }/// <summary>/// 出生日期/// </summary>public DateTime Birthday { get; set; }}

方案一:通过添加AddUserVerifyAttribute来实现

    [AttributeUsage(AttributeTargets.All, AllowMultiple = false)]public class AddUserVerifyAttribute : ValidationAttribute{protected override ValidationResult IsValid(object value, ValidationContext validationContext){var user = (AddUserinfoVm)validationContext.ObjectInstance;//user 变量表示 AddUserinfoVm 对象,其中包含表单提交中的数据var date = (DateTime)value;if (date > DateTime.Now){return new ValidationResult("出生日期不能大于当前时间");}if (user.UserName == user.EmployeeNo){return new ValidationResult("名称和工号不能一样");}return ValidationResult.Success;}}

使用方法

        [AddUserVerify]public DateTime Birthday { get; set; }

方案二:模型类中继承IValidatableObject,并实现Validate方法

        /// <summary>/// 属性级别的自定义验证/// </summary>/// <param name="validationContext"></param>/// <returns></returns>   public IEnumerable<ValidationResult> Validate(ValidationContext validationContext){if (Birthday > DateTime.Now){yield return new ValidationResult("出生日期不能大于当前时间", new[] { nameof(Birthday) });}if (UserName == EmployeeNo){yield return new ValidationResult("名称和工号不能一样", new[] { nameof(UserName), nameof(EmployeeNo) });}}

请求参数:

{"userName": "string","passWord": "string","employeeNo": "string","birthday": "2021-06-15T14:34:52.192Z"
}

输出错误信息

{"errors": {"Birthday": ["出生日期不能大于当前时间"],"UserName": ["名称和工号不能一样"],"EmployeeNo": ["名称和工号不能一样"]},"type": "https://tools.ietf.org/html/rfc7231#p-6.5.1","title": "One or more validation errors occurred.","status": 400,"traceId": "00-18854d59f6b6fc48b5c4c6a6dbe3802c-ba23f594f351a64d-00"
}

ModelState.IsValid

通过该方法可以实现对请求类验证是否满足要求并做出相应的响应。

如果已经使用[ApiController]标识,那么该方法就不在需要。

        [HttpPost]public ActionResult Add([FromBody] AddModelVerify dto){//对请求类进行验证特性if (ModelState.IsValid){//对请求类的值做出修改dto.UserName = "azrng";if (!TryValidateModel(dto)){//重新运行验证失败return Ok("修改值后验证失败");}return Ok("验证成功");}else{ModelState.AddModelError(string.Empty, "输入有误");}return Ok("");}

禁用验证

    /// <summary>/// 创建不会将任何字段标记为无效的 IObjectModelValidator 实现。/// </summary>public class NullObjectModelValidator : IObjectModelValidator{public void Validate(ActionContext actionContext,ValidationStateDictionary validationState, string prefix, object model){// 该方法故意为空}}

Startup.ConfigureServices中注入,以便替换依赖项注入容器中的默认 IObjectModelValidator 实现。

services.AddSingleton<IObjectModelValidator, NullObjectModelValidator>();

统一模型拦截器

增加ModelActionFiter过滤器

    public class ModelActionFiter : ActionFilterAttribute{public override void OnActionExecuted(ActionExecutedContext context){}public override void OnActionExecuting(ActionExecutingContext context){if (!context.ModelState.IsValid){var errorResults = new List<ErrorResultDTO>();foreach (var item in context.ModelState){var result = new ErrorResultDTO{Field = item.Key,};foreach (var error in item.Value.Errors){if (!string.IsNullOrEmpty(result.Message)){result.Message += '|';}result.Message += error.ErrorMessage;}errorResults.Add(result);}context.Result = new BadRequestObjectResult(new{Code = StatusCodes.Status400BadRequest,Errors = errorResults});}}public class ErrorResultDTO{/// <summary>/// 参数领域/// </summary>public string Field { get; set; }/// <summary>/// 错误信息/// </summary>public string Message { get; set; }}}

参考文档:https://www.cnblogs.com/minskiter/p/11601873.html

ConfigureServices中注册过滤器并禁用默认的自动模型验证

    services.AddControllers(options =>{options.Filters.Add<ModelActionFiter>(); //注册过滤器 }).AddNewtonsoftJson().ConfigureApiBehaviorOptions(options =>{//[ApiController] 默认自带有400模型验证,且优先级比较高,如果需要自定义模型验证,则需要先关闭默认的模型验证options.SuppressModelStateInvalidFilter = true; });

ASP.NET Core MVC 使用 ModelStateInvalidFilter 操作筛选器来执行自定义验证。

输出结果

{"code": 400,"errors": [{"field": "PassWord","message": "密码不能为空|密码必须大于6位"},{"field": "UserName","message": "名称不能为空|名称不能小于6位"}]
}

参考文档

模型绑定:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/model-binding?view=aspnetcore-5.0

禁用绑定源推理:https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-5.0#disable-inference-rules

禁用验证:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-5.0#disable-validation

禁用自动400响应:https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-5.0#disable-automatic-400-response

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/301604.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每日一笑 | 大学教室的真实写照...

全世界只有3.14 % 的人关注了数据与算法之美&#xff08;图片来源于网络&#xff0c;侵权删&#xff09;

linux调用v4l2获取视频,嵌入式Linux:V4L2视频采集操作流程和接口说明

一般操作流程(视频设备)&#xff1a;1. 打开设备文件。 int fdopen("/dev/video0",O_RDWR);2. 取得设备的capability&#xff0c;看看设备具有什么功能&#xff0c;比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability3. 选择视频输入…

面向对象技术——UML

UML&#xff0c;统一建模语言是一种可视化建模语言。 UML包括九种类型的图&#xff1a;用例图&#xff0c;类图&#xff0c;对象图&#xff0c;顺序图&#xff0c;协作图&#xff0c;状态图&#xff0c;活动图&#xff0c;构件图&#xff0c;及部署图&#xff0c;各种图示系统在…

美国返还中国文物,阿里谣言粉碎机获奖,教育部规范研究生培养,腾讯严打微信跑分活动,推动降低港澳漫游费,这就是今天的大新闻。...

今天是3月1日农历正月廿五今天星期五相信大家都很舍不得放下工作下面是今天的大新闻美国返还361件中国文物&#xff08;中国日报&#xff09;当地时间2月28日&#xff0c;美国政府向中国返还361件&#xff08;套&#xff09;流失文物。这些中国流失文物&#xff0c;由美国联邦调…

Linux怎么更新镜像,利用 Zsync 更新已有的 Ubuntu ISO 镜像

对! 是升级iso镜像, 不是升级系统. 从旧的镜像升级到新的镜像.可能有点迟了~大家都down好了镜像~ 我现在才有心情和时间写blog哦~由alpha的iso升到正式版都可以. 呃~ 当然,估计由alpha开始的话,下载量也与直接下载正式版区别不大~这么多人下载, 速度当然会慢喇~ 用zsync来升级镜…

你有做 Code Review 吗?

在代码的编写中有一个很重要的环节&#xff0c;经常会被忽视&#xff0c;那就是 Code Review ,据说在 Facebook、Google 这种互联网大公司&#xff0c;要求每一个提交都必须通过审查&#xff0c;对于每个工程师来说 Code Review 是一项十分重要的工作&#xff0c;甚至比写代码本…

PhotoShop CS5制作残旧的印章效果

编者按&#xff1a;不少网友喜欢个性印章效果&#xff0c;因此常常搜索个性印章在线制作。其实&#xff0c;Photoshop就可以完成个性印章制作。事实上&#xff0c;使用 Photoshop制作残旧的印章效果文字有多种方法&#xff0c;例如可以使用云彩滤镜。本文作者介绍了另一种实现方…

限时秒杀┃秒杀90%的玩具,让孩子爱上科学的彩虹实验2来了!

▲数据汪特别推荐点击上图进入玩酷屋之前小木有推荐过“彩虹实验”&#xff0c;这款是可以让孩子在探索中能够独立思考&#xff0c;主动地构建知识库&#xff0c;培养创造力。&#xff08;传送门&#xff09;让孩子们在家开展科学游戏&#xff0c;既能提升动手能力&#xff0c;…

感想四

2019独角兽企业重金招聘Python工程师标准>>> 随着年龄的增长&#xff0c;对人对事物的认知就越真&#xff0c;包括知识也是如此。&#xfeff; 很多年前&#xff0c;在软件开发领域中发生了一个有趣的转变&#xff0c;软件变成了系统中最为昂贵、最为重要的部分。从…

.NET Worker Service 作为 Windows 服务运行及优雅退出改进

上一篇文章我们了解了如何为 Worker Service 添加 Serilog 日志记录&#xff0c;今天我接着介绍一下如何将 Worker Service 作为 Windows 服务运行。我曾经在前面一篇文章的总结中提到过可以使用 sc.exe 实用工具将 Worker Service 安装为 Windows 服务运行&#xff0c;本文中我…

私有云存储 linux,搭建nextcloud私有云存储网盘

本文将要为您介绍的是搭建nextcloud私有云存储网盘,具体完成步骤:简介&#xff1a;搭建个人云存储一般会想到ownCloud&#xff0c;堪称是自建云存储服务的经典。而Nextcloud是ownCloud原开发团队打造的号称是“下一代”存储.真正试用过后就由衷地赞同这个Nextcloud&#xff1a;…

我报了个税,隐私就被扒光了?

全世界只有3.14 % 的人关注了数据与算法之美1月14日&#xff0c;据外媒报道&#xff0c;美国参议院金融委员会正在向美国财政部和国税局施压&#xff0c;要求他们采取网络安全措施。相关数据显示&#xff0c;2015年美国约有700,000名纳税人身份信息遭到泄露&#xff0c;为了解决…

C# 外接(网口)双摄像头视频获取

【注意事项】------------------------------------1. 更新设备网络SDK时&#xff0c;SDK开发包【库文件】里的HCNetSDK.dll、HCCore.dll、PlayCtrl.dll、SuperRender.dll、AudioRender.dll、HCNetSDKCom文件夹、ssleay32.dll、libeay32.dll、hlog.dll、hpr.dll、zlib1.dll、lo…

Visual Studio 2022 Preview 1 和.NET 6 Preview 5 正式发布

具有里程碑意义的Visual Studio 2022 Preview 1正式发布&#xff0c;重点是64位&#xff0c;而没有增加新功能&#xff0c;并且同时也发布了.NET 6 Preview 5。https://devblogs.microsoft.com/visualstudio/visual-studio-2022-preview-1-now-available/https://devblogs.micr…

每日一笑 | 你知道你爸妈当年是怎么在一起的吗?

全世界只有3.14 % 的人关注了数据与算法之美&#xff08;图片来源于网络&#xff0c;侵权删&#xff09;

每日一笑 | 你知道程序媛最“大”的烦恼是什么吗?

全世界只有3.14 % 的人关注了数据与算法之美&#xff08;图片来源于网络&#xff0c;侵权删&#xff09;

什么?他居然想在DLL中放毒!

dotnet/runtime有一个issue[1]讨论了如何使用ModuleInitializer投放恶意代码&#xff0c;但是微软的开发经理表示不背这个锅&#xff01; 什么是ModuleInitializer&#xff1f;ModuleInitializerAttribute[2]是在.NET5.0中新增加的API。它的作用是应用于编译中的任意数量的静态…

为什么说人工智能是一个大谎言

全世界只有3.14 % 的人关注了数据与算法之美人工智能是一个很大的谎言。或者往轻了说&#xff0c;它是一个混淆概念。往重了说&#xff0c;是用来欺骗大众的流行语&#xff0c;并且流行度非常高。其实真正的内涵是“机器学习”。所以&#xff0c;真正强大的&#xff0c;每个人都…

NET问答: ThreadStatic 和 ThreadLocalT 哪一个更好 ?

咨询区 user2341923&#xff1a;[ThreadStatic] 被设计成特性&#xff0c; ThreadLocal<T> 被设计成泛型&#xff0c;为什么会有这两种设计方案呢 &#xff1f;谁能告诉我他们的优缺点各是什么&#xff1f;是不是泛型的方式更好一点&#xff1f;回答区 Sanjeev&#xff1…

指针变量的定义与引用

2019独角兽企业重金招聘Python工程师标准>>> void main() { int a 5,b3; int *p; p &a; b*P5; printf("%d\n", b); *p 4; printf("%d,%d\n",a,*p); } 输出结果&#xff1a;10 4 4 之前在学校的时候就没怎么搞懂&#xff0c; 现在…