命名空間
System.ComponentModel.DataAnnotations
全局模型驗證,統一api響應
1、WebApi項目下新建文件夾【Custom】,新建文件夾【Filter】定義類FieldActionFilter

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Linq;
namespace Test.WebApi.Custom.Filter
{
/// <summary>
/// 驗證參數
/// </summary>
public class FieldActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
//throw new NotImplementedException();
}
public void OnActionExecuting(ActionExecutingContext context)
{
ResponseDto result = new();
if (!context.ModelState.IsValid)
{
var errorMessage = context.ModelState.Values.SelectMany(v => v.Errors).FirstOrDefault();
result.Success = false;
result.Message = errorMessage.ErrorMessage;
context.Result = new JsonResult(result);
}
}
}
}
2、StartUp注冊服務

services.AddMvc(options =>
{
options.Filters.Add<FieldActionFilter>();//Dto參數驗證
});
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true; // 使用自定義模型驗證【Api接口需要添加才能生效】
});
基礎屬性
| 屬性名稱 | 描述 |
|---|---|
| Required | 標識該屬性為必需參數,不能為空 |
| StringLength | 標識該字符串有長度限制,可以限制最小或最大長度 |
| MaxLength | 不為null時,不能超過最大字符串長度 |
| MixLength | 不為null時,不能少於最小字符串長度 |
| Range | 標識該屬性值范圍,通常被用在數值型和日期型 |
| Compare | 比較 - 與制定的字段值進行比較 具體見代碼 [Compare(“MyOtherProperty”)]兩個屬性必須相同值,比如我們要求用戶重復輸入兩次密碼時有用 |
| RegularExpression | 標識該屬性將根據提供的正則表達式進行對比驗證 |
| Remote | 服務端驗證 |
| CustomValidation | 標識該屬性將按照用戶提供的自定義驗證方法,進行數值驗證 |
| 以下使用於表單:沒有測試過,如需使用請自己測試 | |
| DisplayName | 顯示名 – 定義表單字段的提示名稱,一般用於在PropertyGrid或者DataGridView里顯示這個對象時,列名會自動顯示DisplayName定義的值,而不是字段名 |
| Bind | 綁定 – 列出在將請求參數綁定到模型的時候,包含和不包含的字段 |
| ScaffoldColumn | 支架列 - 在編輯表單的時候,需要隱藏起來的的字符 [ScaffoldColumn(true|false)] |
| DataType | 在前端顯示的文本框類型 |
| Editable | [Editable(false)] //放在主鍵上顯示不可修改 |
Validations---這個是所有驗證屬性的基類
基礎屬性【Required、Range、StringLength、Compare】
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Test.Application.Employee.Dto
{
public class AddEmployeeDto
{
/// <summary>
/// 職員姓名不能為空
/// </summary>
[Required(ErrorMessage = "職員姓名不能為空")]
public string EmpName { get; set; }
/// <summary>
/// 密碼
/// </summary>
[Required(ErrorMessage = "密碼不能為空")]
[StringLength(12, MinimumLength = 6, ErrorMessage = "密碼只能是6到12位")]
public string Pwd { get; set; }
/// <summary>
/// 確認密碼
/// </summary>
[Compare("Pwd", ErrorMessage = "兩次密碼輸入的不一致")]
public string RPwd { get; set; }
public string AAA { get; set; }
/// <summary>
/// 備注
/// </summary>
[StringLength(200, ErrorMessage = "備注不能超過200字")]
public string Remark { get; set; }
/// <summary>
/// 職員編號
/// </summary>
[Required] //Swagger 字段后有一個*表示必填,實際上在傳參的時候不填會有默認值0,這個沒有起到實際意義上的驗證效果
[Range(1, int.MaxValue, ErrorMessage = "職員編號不能為空")] //適用於自增長Id
public int EmpId { get; set; }
/// <summary>
/// 備注2
/// </summary>
[StringLength(200, MinimumLength = 1, ErrorMessage = "備注2只能是1至200字")] //可以為null,不能為""
public string Remark2 { get; set; }
/// <summary>
/// 年齡
/// </summary>
[Range(18, 60, ErrorMessage = "年齡只能是18到60歲")] //可null類型,可以為null
public int? Age { get; set; }
/// <summary>
/// 價格
/// </summary>
[Range(9.99, 99.99, ErrorMessage = "價格只能是9.99至99.99")]
public double Price { get; set; }
/// <summary>
/// 生日
/// </summary>
[Range(typeof(DateTime), "2021-03-02", "2021-04-02", ErrorMessage = "日期只能是3月2號到4月2號")] //2021-04-02 00:00:01 不符合
public DateTime Birthday { get; set; }
}
}
基礎屬性【RegularExpression】
using System.ComponentModel.DataAnnotations;
namespace Test.Application.Employee.Dto
{
/// <summary>
/// 常工規則
/// 1、手機號碼
/// 通用校驗規則:^1[3456789]\d{9}$
/// 詳細手機號校驗規則:^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$
/// 驗證手機號和固定電話:^((0\d{2,3}-\d{7,8})|(1[34578]\d{9}))$
/// 2、郵箱格式
/// [\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?
/// 3、用戶名校驗
/// 6-16位的包含大小寫字母、數字、特殊符號- _ 的用戶名:^[a-zA-Z0-9_-]{6,16}$
/// 6-20位字母和數字組合:^(?![0-9]*$)(?![a-zA-Z]*$)[a-zA-Z0-9]{6,20}$
/// 4、密碼強度校驗
/// 密碼強度正則,最少6位,包括至少1個大寫字母,1個小寫字母,1個數字,1個特殊字符:^.*(?=.{6,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$
/// 請輸入6-20位英文字母、數字或者符號(除空格),且字母、數字和標點符號至少包含兩種:^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$)([^\u4e00-\u9fa5\s]){6,20}$
/// 5、整數校驗
/// 正整數正則:^\d+$
/// 負整數正則:^-\d+$
/// 整數正則:^-?\d+$
/// 6、身份證驗證
/// (^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)
/// 7、合法url校驗
/// ^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$
/// 8、驗證輸入內容是否包含英文數字及下划線
/// ^[_a-zA-Z0-9]+$
/// 9、驗證是否兩位小數
/// (^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)
/// 10、中文校驗
/// ^[\u0391-\uFFE5A-Za-z]+$
/// 11、純數字校驗
/// ^\d+$|^\d+[.]?\d+$
/// 12、最多一位小數
/// ^[0-9]+([.]{1}[0-9]{1})?$
/// 13、ip地址校驗
/// ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
/// 14、包含中文的校驗
/// [\u4e00-\u9fa5]
/// 15、內容只能由英文、數字、下划線組成
/// ^\w+$
/// 16、內容只能包含英文字母和數字
/// ^[a-z0-9]+$
/// 17、固定電話
/// ^((0\d{2,5}-)|0\d2,50\d2,5)?\d{7,8}(-\d{3,4})?$
/// </summary>
public class ModifyEmployeeDto
{
/// <summary>
/// 郵箱
/// </summary>
[RegularExpression(@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4})$", ErrorMessage = "郵箱格式不正確")]
public string Email { get; set; }
/// <summary>
/// 身份證
/// </summary>
[RegularExpression(@"^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$", ErrorMessage = "錯誤")]
public string IDCard { get; set; }
}
}
基礎屬性【Remote】【沒有查到相關資料,有知道的小伙伴請在評論區留下代碼】
自定義驗證
在XXX.Service項目中新建文件夾【Base/Validation】新建類:EnumCheckAttribute
using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
namespace Test.Application.Base.Validation
{
/// <summary>
/// 自定義驗證
/// 驗證是否是有效的枚舉值
/// </summary>
public class EnumCheckAttribute : ValidationAttribute
{
/// <summary>
/// 驗證的枚舉類Type
/// </summary>
public Type TypeClass { get; set; }
/// <summary>
/// 枚舉范圍外的鍵,如有更好的方案請留言
/// </summary>
public int OtherKey { get; set; } = int.MinValue;
/// <summary>
/// 枚舉范圍外的值
/// </summary>
public string OtherValue { get; set; }
public override bool IsValid(object value)
{
string enumValue = Enum.GetName(TypeClass, value);
if (enumValue == null)
{
if (OtherKey == Convert.ToInt32(value))
{
return true;
}
return false;
}
return true;
}
/// <summary>
/// 提示中,添加枚舉的鍵值對,如有更好的方案請留言
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public override string FormatErrorMessage(string name)
{
return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, new object[] { EnumToJson() });
}
/// <summary>
/// 如有更好的方案請留言
/// </summary>
/// <returns></returns>
private string EnumToJson()
{
if (!TypeClass.IsEnum)
throw new InvalidOperationException("enum expected");
var results = Enum.GetValues(TypeClass).Cast<object>().ToDictionary(enumValue => (int)enumValue, enumValue => enumValue.ToString());
//if (OtherKey != int.MinValue)
//{
// results.Add(OtherKey, OtherValue);
//}
//return string.Format("{0}", JsonConvert.SerializeObject(results)).Replace("\"", "");
string result = "{";
if (OtherKey != int.MinValue)
{
result += OtherValue + ":" + OtherKey + ",";
}
foreach (var key in results.Keys)
{
result += results[key] + ":" + key + ",";
}
result = result.Remove(result.Length - 1);
result += "}";
return result;
}
}
}
使用
using Test.Application.Base.Validation;
using Test.Model;
namespace Test.Application.Employee.Dto
{
public class QueryEmployeeDto
{
/// <summary>
/// 性別
/// </summary>
[EnumCheck(TypeClass = typeof(Genders), OtherKey = -1, OtherValue = "全部", ErrorMessage = "性別無效,范圍值為:{0}")]
public int Gender { get; set; }
}
}
