ASP.NET Core搭建多層網站架構【8.1-使用ViewModel註解驗證】

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

摘要:基於ASP.NET Core 3.1 WebApi搭建後端多層網站架構【8.1-使用ViewModel註解驗證】
使用ViewModel註解驗證字段合法性,將ViewModel的字段驗證寫在了ViewModel類內部前端

文章目錄git

此分支項目代碼github

本章節介紹了使用ASP.NET Core自帶的註解驗證前端提交的參數正則表達式

確認MS.Models類庫已引用MS.Component.JwtMS.DbContexts兩個項目數據庫

添加業務操做枚舉和返回值定義

MS.WebCore類庫中新建Core文件夾,在該文件夾中新建ExecuteResult.csExecuteType.cs類:json

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類:async

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註解驗證的使用

項目完成後,以下圖所示

相關文章
相關標籤/搜索