Loading

ASP.NET Core搭建多层网站架构【8.1-使用ViewModel注解验证】

2020/01/29, ASP.NET Core 3.1, VS2019

摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构【8.1-使用ViewModel注解验证】
使用ViewModel注解验证字段合法性,将ViewModel的字段验证写在了ViewModel类内部

文章目录

此分支项目代码

本章节介绍了使用ASP.NET Core自带的注解验证前端提交的参数

确认MS.Models类库已引用MS.Component.JwtMS.DbContexts两个项目

添加业务操作枚举和返回值定义

MS.WebCore类库中新建Core文件夹,在该文件夹中新建ExecuteResult.csExecuteType.cs类:

ExecuteResult.cs

namespace MS.WebCore.Core
{
    /// <summary>
    ///  执行返回结果
    /// </summary>
    public class ExecuteResult
    {
        public virtual ExecuteResult Set(bool isSucceed, string message)
        {
            IsSucceed = isSucceed;
            Message = message;
            return this;
        }
        /// <summary>
        /// 设定错误信息
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public virtual ExecuteResult SetFailMessage(string message)
        {
            return Set(false, message);
        }
        public virtual ExecuteResult SetFail()
        {
            return Set(false, string.Empty);
        }
        public ExecuteResult(bool isSucceed, string message)
        {
            Set(isSucceed, message);
        }
        /// <summary>
        /// 如果是给字符串,表示有错误信息,默认IsSucceed=false
        /// </summary>
        /// <param name="message"></param>
        public ExecuteResult(string message)
        {
            Set(false, message);
        }
        /// <summary>
        /// 如果是空的,没有信息,默认IsSucceed=true
        /// </summary>
        public ExecuteResult()
        {
        }
        /// <summary>
        /// 执行是否成功
        /// 默认为True
        /// </summary>
        public bool IsSucceed { get; set; } = true;
        /// <summary>
        /// 执行信息(一般是错误信息)
        /// 默认置空
        /// </summary>
        public string Message { get; set; } = string.Empty;
    }
    /// <summary>
    /// 执行返回结果
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ExecuteResult<T> : ExecuteResult
    {
        public ExecuteResult<T> Set(bool isSucceed, string message, T result)
        {
            IsSucceed = isSucceed;
            Message = message;
            Result = result;
            return this;
        }
        public ExecuteResult<T> SetData(T data)
        {
            return Set(true, string.Empty, data);
        }
        public new ExecuteResult<T> SetFail()
        {
            return Set(false, string.Empty, default);
        }
        /// <summary>
        /// 设定错误信息
        /// 如果T正好也是string类型,可能set方法会存在用错的时候,所以取名SetMessage更明确
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public new ExecuteResult<T> SetFailMessage(string message)
        {
            return Set(false, message, default);
        }
        public ExecuteResult()
        {
        }
        public ExecuteResult(string message)
        {
            Set(false, message);
        }
        public ExecuteResult(bool isSucceed, string message)
        {
            Set(isSucceed, message);
        }
        public ExecuteResult(T result)
        {
            SetData(result);
        }

        public T Result { get; set; }
    }
}

这个类是定义了业务相关方法调用的返回值结果,包含了是否成功、错误信息,也可以拓展泛型夹带其他内容

ExecuteType.cs

namespace MS.WebCore.Core
{
    /// <summary>
    /// 表示操作数据库类型
    /// </summary>
    public enum ExecuteType
    {
        /// <summary>
        /// 读取资源
        /// </summary>
        Retrieve,
        /// <summary>
        /// 创建资源
        /// </summary>
        Create,
        /// <summary>
        /// 更新资源
        /// </summary>
        Update,
        /// <summary>
        /// 删除资源
        /// </summary>
        Delete
    }
}

这个枚举定义了业务操作的类型,对应数据库的CRUD

新建ViewModel

MS.Models类库中新建ViewModel文件夹,在该文件夹中新建RoleViewModel.cs类:

using MS.DbContexts;
using MS.Entities;
using MS.UnitOfWork;
using MS.WebCore.Core;
using System.ComponentModel.DataAnnotations;

namespace MS.Models.ViewModel
{
    public class RoleViewModel
    {
        public long Id { get; set; }
        [Display(Name = "角色名称")]
        [Required(ErrorMessage = "{0}必填")]
        [StringLength(16, ErrorMessage = "不能超过{0}个字符")]
        [RegularExpression(@"^[a-zA-Z0-9_]{4,16}$", ErrorMessage = "只能包含字符、数字和下划线")]
        public string Name { get; set; }
        [Display(Name = "角色显示名")]
        [Required(ErrorMessage = "{0}必填")]
        [StringLength(50, ErrorMessage = "不能超过{0}个字符")]
        public string DisplayName { get; set; }
        [Display(Name = "备注")]
        [StringLength(4000, ErrorMessage = "不能超过{0}个字符")]
        public string Remark { get; set; }

        public ExecuteResult CheckField(ExecuteType executeType, IUnitOfWork<MSDbContext> unitOfWork)
        {
            ExecuteResult result = new ExecuteResult();
            var repo = unitOfWork.GetRepository<Role>();

            //如果不是新增角色,操作之前都要先检查角色是否存在
            if (executeType != ExecuteType.Create && !repo.Exists(a => a.Id == Id))
            {
                return result.SetFailMessage("角色不存在");
            }

            //针对不同的操作,检查逻辑不同
            switch (executeType)
            {
                case ExecuteType.Delete:
                    //删除角色前检查角色下还没有员工
                    if (unitOfWork.GetRepository<User>().Exists(a => a.RoleId == Id))
                    {
                        return result.SetFailMessage("还有员工正在使用该角色,无法删除");
                    }
                    break;
                case ExecuteType.Update:
                    //如果存在Id不同,角色名相同的实体,则返回报错
                    if (repo.Exists(a => a.Name == Name && a.Id != Id))
                    {
                        return result.SetFailMessage($"已存在相同的角色名称:{Name}");
                    }
                    break;
                case ExecuteType.Create:
                default:
                    //如果存在相同的角色名,则返回报错
                    if (repo.Exists(a => a.Name == Name))
                    {
                        return result.SetFailMessage($"已存在相同的角色名称:{Name}");
                    }
                    break;
            }
            return result;//没有错误,默认返回成功
        }
    }
}

说明

  • Display是该字段的显示名称
  • Required注解标记该字段必填,不可为空
  • StringLength注解标记该字段长度
  • RegularExpression注解是正则表达式验证
  • 还有个Range注解特性是验证值的范围的,这里没用到

除了注解,我把对象字段的逻辑验证写在了ViewModel中,没有把它放在业务层是因为,我认为对象字段本身的合法性和对象是强相关的,就和注解直接写在ViewModel中而不是Service中一样,所以把字段的验证也写在了ViewModel里

对象字段的逻辑验证我区分了操作类型,新增时检查角色名是否有重复;删除时检查是否还有用户使用该角色;更新时检查用户提交的角色是否存在于数据库中

新建Controller

MS.WebApi应用程序中,Controllers文件夹下新建RoleController.cs:

using Microsoft.AspNetCore.Mvc;
using MS.Models.ViewModel;
using MS.WebCore.Core;
using System.Threading.Tasks;

namespace MS.WebApi.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class RoleController : ControllerBase
    {
        [HttpPost]
        public async Task<ExecuteResult> Post(RoleViewModel viewModel)
        {
            return new ExecuteResult();
        }
    }
}

删除WeatherForecastController.csWeatherForecast.cs两个类

完成后,启动项目,打开Postman
按之前的方法,在MSDemo集合中添加一个新的POST请求Role,URL为http://localhost:5000/role
切换到Body选项卡,选择raw,切换为json格式数据:

{
    "Name": "",
    "DisplayName": ""
}

点击发送,可以看到提示字段必填了

输入正确后,能返回true

Name输入超过16个字符错误,验证正则表达式等不再演示

以上便是ViewModel注解验证的使用

项目完成后,如下图所示

posted @ 2020-01-29 14:54  kasnti  阅读(1764)  评论(0编辑  收藏  举报