ASP.NET 中验证的自定义返回和统一社会信用代码的内置验证实现

简介: 本文介绍 ASP.NET 中内置的验证功能,并介绍如何自定义验证返回信息,最后以统一社会信用代码为例,实现自定义的数据验证。

DataAnnotations 命名空间提供常用的内置验证特性,可通过声明方式应用于类或属性。我们不需要编写复杂的逻辑,仅需要指定一次,即可应用到整个项目中。代码量的减少,意味着更少的出错,也更易于测试和维护。指定了验证特性的模型会进行强制执行这些验证,有助于提升应用的可靠性,同时保证你在忘记编写某些验证逻辑时,防止你通过应用提交错误的数据到数据库。下面我们来实际使用一下:

项目演示

创建项目

首先我们创建一个 ASP.NET Core Web API 项目,记得不要使用最小 API,因为最小 API 没有对验证的内置支持,参见《最小 API 与具有控制器的 API 之间的差异》

创建项目

创建用户注册信息接收类

接着我们创建一个 OrgRegInfo 类,用于接收用户的注册信息,并把基本验证规则通过内置验证功能进行声明。更多内置特性和使用可参考官方文档《模型验证》

public class OrgRegInfo
{
    /// <summary>
    /// 用户名
    /// </summary>
    [StringLength(50, MinimumLength = 3, ErrorMessage = "用户名长度为3-50个字符")]
    [Required(ErrorMessage = "请填写用户名")]
    public string? name { get; set; }

    /// <summary>
    /// 邮箱
    /// </summary>
    [StringLength(100, ErrorMessage = "邮箱最大支持100个字符,请更换邮箱")]
    [EmailAddress(ErrorMessage = "邮箱格式不正常")]
    [Required(ErrorMessage = "请填写公司邮箱")]
    public string? email { get; set; }

    /// <summary>
    /// 密码
    /// </summary>
    [StringLength(50, MinimumLength = 8, ErrorMessage = "密码长度为8-50个字符")]
    [Required(ErrorMessage = "请填写密码")]
    public string? pwd { get; set; }

    /// <summary>
    /// 信用代码
    /// </summary>
    [Required(ErrorMessage = "请填写统一社会信用代码")]
    public string? orgid { get; set; }
    /// <summary>
    /// 企业名称
    /// </summary>
    [StringLength(80, MinimumLength = 2, ErrorMessage = "企业名称2-80个字符")]
    [Required(ErrorMessage = "请填写工商注册的企业名称")]
    public string? orgname { get; set; }
    /// <summary>
    /// 联系人信息
    /// </summary>
    [StringLength(20, MinimumLength = 2, ErrorMessage = "企业联系人2-20个字符")]
    [Required(ErrorMessage = "请填写企业联系人")]
    public string? orguser { get; set; }

    /// <summary>
    /// 联系人电话
    /// </summary>
    [Phone(ErrorMessage ="联系人电话格式有误")]
    [Required(ErrorMessage = "请填写联系人电话")]
    public string? orgphone { get; set; }
}

创建测试 Controller

Controllers文件夹下新建一个 HomeController.cs,内容如下,该方法可以通过 POST 方法获取到我们的输入,简单的验证我们的前面的声明是否有效,如果有效则返回我们的输入信息。

using Microsoft.AspNetCore.Mvc;

namespace ValidationDemo.Controllers
{
    [Route("api")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        [HttpPost]
        public OrgRegInfo Index([FromBody] OrgRegInfo orgreginfo)
        {
            return orgreginfo;
        }
    }
}

执行测试

准备完成,我们先启动项目,在 Swagger UI 中,我们直接调用刚刚写好的 API,先不修改传入的参数。

调用API

默认直接发送的话,请求的数据如下:

{
  "name": "string",
  "email": "user@example.com",
  "pwd": "stringst",
  "orgid": "string",
  "orgname": "string",
  "orguser": "string",
  "orgphone": "string"
}

不出意外的话,我们就可以看到执行数据验证的返回效果了,接口直接报 400:

验证错误

错误返回内容如下:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-d2cd5da922b883bdf60a464ba305d722-d5ce4691eb28a064-00",
  "errors": {
    "orgphone": [
      "联系人电话格式有误"
    ]
  }
}

统一错误返回

一般来说我们都会要求一定的接口返回格式,便于前端的统一处理,比如:

{
    "success" : false,
    "code" : 20000,
    "msg" : "str",
    "data": "Any"
}

对应错误返回使用 200 还是 400 的 HTTP 状态码,项目中协商一致按照统一标准即可。我个人在写前端的时候,一般是 200 派的。不过,错误的状态码,可以通一进入错误的处理逻辑进行处理,也是比较好的方案,主要还是看个人喜好,有的前端框架,比如使用前端低代码的时候,会要求你后端出错要怎么返回。

如何统一错误返回呢?

首先我们需要创建一个自定义的过滤器,来处理验证出错后的返回,关于筛选器的更详细的介绍,可查阅官网的文档《ASP.NET Core 中的筛选器》

新建一个 ModelValidateActionFilterAttribute.cs 文件,继承 ActionFilterAttribute 重写 OnActionExecuting :

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;

namespace ValidationDemo
{

    public class ModelValidateActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                //获取验证失败的模型字段
                var errors = context.ModelState
                    .Where(e => e.Value.Errors.Count > 0)
                    .Select(e => e.Value.Errors.First().ErrorMessage)
                    .ToList();

                // | 简单组合一下多处错误
                var str = string.Join("|", errors);

                //设置返回内容
                var result = new
                {
                    success = false,
                    code = 20000,
                    msg = str,
                    data = ""
                };

                context.Result = new BadRequestObjectResult(result);
            }

        }
    }

}

接下来我们需要在 Program.cs 文件中进行一些配置,使其生效。

我们需要先关闭默认的筛选器 BadRequestObjectResult, 并添加上我们自己刚刚写好的过滤器。

//关闭默认模型验证
builder.Services.Configure<ApiBehaviorOptions>(opt => opt.SuppressModelStateInvalidFilter = true);
builder.Services.AddControllers(opt =>
{
    //添加过滤器
    opt.Filters.Add<ModelValidateActionFilterAttribute>();
});

最后,我们再次进行刚刚的测试,可以看到,返回已经是我们刚刚在过滤器里面设置好的格式了。

修改结果

自定义验证规则

内置的验证虽然满足了基本的使用需求,但如何自定义验证规则呢?下面我们就以统一社会信用代码为例,介绍如何自定义内置验证规则。

验证规则介绍

我们要验证统一社会信用代码,首先就需要了解它。统一社会信用代码是一组长度为18位的用于法人和其他组织身份识别的代码。相当于我们自己的身份证号,是推动社会信用体系建设的一项重要改革措施。在设计公司信息的相关数据存储时,我们可能需要验证统一社会信用代码的真实性。

统一社会信用代码由18位数字或者大写字母组成,但是字母不包括 I、O、Z、S、V一共由五部分组成,下表就是其详细的组成。

代码序号

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

代码

x

x

x

x

x

x

x

x

x

x

x

x

x

x

x

x

x

x

说明

登记管理部门代码1

机构类别代码1

登记管理机关行政区划码6

主体标识码(组织机构代码)9

校验码1

如何验证,我们只需要按照官网的《统一社会信用代码数据错误类型及其标准提法(错码)》 处理即可,校验码的验证也可以在官网搜索到 GB 32100-2015 的规范性文件。

校验码验证规则

编码实现

我们创建一个 SocialCreditCodeAttribute.cs 文件,继承 Attribute, IModelValidator 实现对统一社会信用代码的数据验证,具体内容如下:

using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;

namespace ValidationDemo
{
    public class SocialCreditCodeAttribute : Attribute, IModelValidator
    {
        // 自定义一个异常信息属性
        public string ErrorMessage { get; set; }
        public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
        {
            var code = context.Model as string;

            // 长度不为18
            if (code == null || code.Length != 18)
            {
                // 抛出异常信息集合
                return new List<ModelValidationResult>
                {
                    new ModelValidationResult(string.Empty,ErrorMessage)
                };
            }

            code = code.ToUpper();
            int[] factor = { 1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28 };
            string str = "0123456789ABCDEFGHJKLMNPQRTUWXY";
            int total = factor.Select((p, i) => p * str.IndexOf(code[i])).Sum();
            int index = total % 31 == 0 ? 0 : (31 - total % 31);
            if (str[index] != code.Last())
            {
                // 抛出异常信息集合
                return new List<ModelValidationResult>
                {
                    new ModelValidationResult(string.Empty,ErrorMessage)
                };
            }
            // 如果没有异常,就抛出一个空集合
            return Enumerable.Empty<ModelValidationResult>();
        }
    }
}

然后我们就可以在 OrgRegInfo 类中对 orgid 属性进行声明。

/// <summary>
/// 信用代码
/// </summary>
[SocialCreditCode(ErrorMessage = "统一社会信用代码效验有误")]
[Required(ErrorMessage = "请填写统一社会信用代码")]
public string? orgid { get; set; }

接下来进行测试,测试可用机构代码 91350100M000100Y43

机构代码测试

最后

完整代码见:https://github.com/sangyuxiaowu/ValidationDemo

相关文章
|
2月前
|
JSON 安全 API
.net 自定义日志类
在.NET中,创建自定义日志类有助于更好地管理日志信息。示例展示了如何创建、配置和使用日志记录功能,包括写入日志文件、设置日志级别、格式化消息等。注意事项涵盖时间戳、日志级别、JSON序列化、线程安全、日志格式、文件处理及示例使用。请根据需求调整代码。
54 13
|
2月前
|
算法 Java 测试技术
使用 BenchmarkDotNet 对 .NET 代码进行性能基准测试
使用 BenchmarkDotNet 对 .NET 代码进行性能基准测试
66 13
|
2月前
|
开发框架 .NET PHP
ASP.NET Web Pages - 添加 Razor 代码
ASP.NET Web Pages 使用 Razor 标记添加服务器端代码,支持 C# 和 Visual Basic。Razor 语法简洁易学,类似于 ASP 和 PHP。例如,在网页中加入 `@DateTime.Now` 可以实时显示当前时间。
|
3月前
|
敏捷开发 缓存 中间件
.NET技术的高效开发模式,涵盖面向对象编程、良好架构设计及高效代码编写与管理三大关键要素
本文深入探讨了.NET技术的高效开发模式,涵盖面向对象编程、良好架构设计及高效代码编写与管理三大关键要素,并通过企业级应用和Web应用开发的实践案例,展示了如何在实际项目中应用这些模式,旨在为开发者提供有益的参考和指导。
52 3
|
4月前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
Android远程连接和登录FTPS服务代码(commons.net库)
61 1
|
4月前
|
Windows
.NET 隐藏/自定义windows系统光标
【10月更文挑战第20天】在.NET中,可以使用`Cursor`类来控制光标。要隐藏光标,可将光标设置为`Cursors.None`。此外,还可以通过从文件或资源加载自定义光标来更改光标的样式。例如,在表单加载时设置`this.Cursor = Cursors.None`隐藏光标,或使用`Cursor.FromFile`方法加载自定义光标文件,也可以将光标文件添加到项目资源中并通过资源管理器加载。这些方法适用于整个表单或特定控件。
|
4月前
|
前端开发 JavaScript C#
CodeMaid:一款基于.NET开发的Visual Studio代码简化和整理实用插件
CodeMaid:一款基于.NET开发的Visual Studio代码简化和整理实用插件
101 0
|
6月前
|
微服务 API Java
微服务架构大揭秘!Play Framework如何助力构建松耦合系统?一场技术革命即将上演!
【8月更文挑战第31天】互联网技术飞速发展,微服务架构成为企业级应用主流。微服务将单一应用拆分成多个小服务,通过轻量级通信机制交互。高性能Java Web框架Play Framework具备轻量级、易扩展特性,适合构建微服务。本文探讨使用Play Framework构建松耦合微服务系统的方法。Play采用响应式编程模型,支持模块化开发,提供丰富生态系统,便于快速构建功能完善的微服务。
71 0
|
6月前
|
SQL 开发框架 .NET
代码更简洁,开发更高效:从零开始使用Entity Framework Core与传统ADO.NET构建数据持久化层的比较
【8月更文挑战第31天】在.NET平台上开发数据驱动应用时,选择合适的ORM框架至关重要。本文通过对比传统的ADO.NET和现代的Entity Framework Core (EF Core),展示了如何从零开始构建数据持久化层。ADO.NET虽强大灵活,但需要大量手写代码;EF Core则简化了数据访问,支持LINQ查询,自动生成SQL命令,提升开发效率。从创建.NET Core项目、定义数据模型、配置`DbContext`到执行数据库操作,EF Core提供了一套流畅的API,使数据持久化层的构建变得简单直接。
89 0
|
6月前
|
Kubernetes 监控 Devops
【独家揭秘】.NET项目中的DevOps实践:从代码提交到生产部署,你不知道的那些事!
【8月更文挑战第28天】.NET 项目中的 DevOps 实践贯穿代码提交到生产部署全流程,涵盖健壮的源代码管理、GitFlow 工作流、持续集成与部署、容器化及监控日志记录。通过 Git、CI/CD 工具、Kubernetes 及日志框架的最佳实践应用,显著提升软件开发效率与质量。本文通过具体示例,助力开发者构建高效可靠的 DevOps 流程,确保项目成功交付。
124 0

热门文章

最新文章