[Abp vNext 入坑分享] - 7.Automapper與validation的使用

二B方案程式設計師發表於2020-05-20

簡要說明

【專案原始碼】

【章節目錄】

本文主要介紹Automapper與Validation的使用方法。首先使用Automapper的目的是引入元件完成entity與dto之間的轉換以達到簡化程式碼的目的。Abp vnext的專案中已經預設新增好此元件了【介紹】,本文只是說一些簡單的用法,更進一步的使用需要到automapper的官網中檢視文件
其次是Validation主要是用於入參校驗,通過對dto標註相應的屬性達到入參校驗的功能。【abp介紹】【官方介紹】

具體步驟

1、Automapper的基礎使用方法,在官方的文件中也有比較清晰的說明,所以就只是簡單的根據官方文件寫一次。然後試執行一下簡單的效果即可。新增相應的mapper使用的Dto,如UserDto,定義好可以對外輸出的屬性。

public class UserDto: EntityDto<Guid>
    {
        /// <summary>
        /// 使用者名稱稱
        /// </summary>
        public string user_name { get; set; }
        /// <summary>
        /// 使用者手機號
        /// </summary>
        public string user_phone { get; set; }
        /// <summary>
        /// 使用者狀態
        /// </summary>
        public int user_status { get; set; }
 
    }

其次在AbpVnext.Learn.Application中找到LearnApplicationAutoMapperProfile類,新增我們需要的mapper,CreateMap<User, UserDto>(); 最後在UserAppServices裡面新增以下程式碼,其中ObjectMapper.Map就是轉換的方法。

 public async Task<UserDto> LoginByUserPhoneAndPwd(string user_phone,string pass_word)
        {
            var user= await _repository.FindAsync(a=>a.user_phone== user_phone&&a.pass_word== pass_word&&a.user_status==0);
            return ObjectMapper.Map<User, UserDto>(user);
        }

除錯程式碼,輸出結果如下,轉換正常:

2、接下來主要介紹一下Validation的用法,官方的Validation是一個簡單與輕量化的元件,主要用資料註解的方式對入參欄位的合法性校驗上。下面我們先來做一個簡單的例子,原來登入介面的入參的引數增加以下[Required]

 /// <summary>
    /// 登入的Dto
    /// </summary>
    public class LoginDto:
    {
        /// <summary>
        /// 使用者手機號
        /// </summary>
        [Required]
        public string user_phone { get; set; }
        /// <summary>
        /// 登入密碼
        /// </summary>
        [Required]
        public string pass_word { get; set; }
    }

然後除錯程式碼,測試相應介面,如下圖丟擲500異常,

再看日誌,顯示的是校驗失敗異常,則表明我們的註解生效了。

3、上面雖然顯示我們的校驗生效了,但是輸出的結果明顯不是我們想要的結果。一般我們需要為校驗失敗的入參輸出統一的code和相應的字串,讓我們的提示比較清晰明瞭,同時也讓前端開發的同事比較明確的知道這個是由於引數校驗引起的問題,然後去修正相關引數。因此,我們需要替換掉abp的原有校驗異常輸出。

首先我們檢視官方文件有說明,校驗異常統一為AbpValidationException 。

因此我們在原來的LeanGlobalExceptionFilter中攔截此型別的異常,並將輸出修改為code=100,如下:

  public void OnException(ExceptionContext context)
        {
            logger.LogError(new EventId(context.Exception.HResult),
           context.Exception,
           context.Exception.Message);
            if (context.Exception is AbpValidationException)
            {
                context.Result = new JsonResult(new  { ode = 100, msg = context.Exception.Message });
            }
            else
            {
                context.Result = new JsonResult(new { code = 500, msg = "系統異常" });
            }
            context.ExceptionHandled = true;
        }

然後我們再來看看除錯介面的輸出結果,如下,則表明我們的替換是成功了。

4、但是這樣就算成功了嗎?當然不是額,abp所有的校驗失敗輸出都是輸出這個:ModelState is not valid! See ValidationErrors for details.這樣的輸出,對我們前端或客戶端同事是非常不友好的輸出,都不知道錯誤原因與相應的欄位,因此我們需要進一步去優化這些資訊以達到最大程度的減少溝通成本的目的。下面我們來看看如何進行操作吧!

4.1、首先我們看一下註解裡面的ValidationAttribute,這個註解包含了欄位ErrorMessage是用於輸出校驗錯誤的欄位的,所以我們可以好好的利用一下這個ErrorMessage來達到我們的目的。把user_phone的[Required]修改成[Required(ErrorMessage ="手機號碼不能為空")]

4.2、我們回到LeanGlobalExceptionFilter這裡,看一下AbpValidationException裡面包含了什麼資訊。

如圖所示,我們看到了ValidationErrors這個欄位是一個List,裡面包含了我們這一個請求入參的整個dto裡所有校驗不通過的資訊列表。這樣就好辦了,修改原來的攔截為以下程式碼:

public void OnException(ExceptionContext context)
        {
            logger.LogError(new EventId(context.Exception.HResult),
           context.Exception,
           context.Exception.Message);
            if (context.Exception is AbpValidationException)
            {
                var validateerros = ((AbpValidationException)context.Exception).ValidationErrors;
                context.Result = new JsonResult(new  { ode = 100, msg = validateerros.Count > 0 ? validateerros[0].ErrorMessage : context.Exception.Message });
            }
            else
            {
                context.Result = new JsonResult(new { code = 500, msg = "系統異常" });
            }
            context.ExceptionHandled = true;
        }

然後除錯程式碼,並測試相應的介面。現在則表示顯示正常了。至於資訊到底是顯示第一個還是最後一個,自己判斷,個人感覺都可以,因為都屬於引數失敗的校驗

4.3、接下來我們使用一下進階的判斷,脫離於單純的欄位校驗,如:假如user_phone等於pass_word的時候丟擲不能相等提示。這個需要dto繼承IValidatableObject然後增加Validate的實現方法,如下:

public IEnumerable<ValidationResult> Validate(
            ValidationContext validationContext)
        {
            if (user_phone == pass_word)
            {
                yield return new ValidationResult(
                    "手機號碼與密碼不能一樣!",
                    new[] { "user_phone", "pass_word" }
                );
            }
        }

然後除錯程式碼,並測試相應的介面,如下則表示替換成功了。。

相關文章