上一篇文章(http://www.javashuo.com/article/p-rscsbfwx-bv.html)使用自定義倉儲完成了簡單的增刪改查案例,有心的同窗能夠看出,咱們的返回參數一塌糊塗,顯得很不友好。html
在實際開發過程當中,每一個公司可能不盡相同,但都大同小異,咱們的返回數據都是包裹在一個公共的模型下面的,而不是直接返回最終數據,在返回參數中,顯示出當前請求的時間戳,是否請求成功,若是錯誤那麼錯誤的消息是什麼,狀態碼(狀態碼能夠是咱們本身定義的值)等等。可能顯得很繁瑣,不必,但這樣作的好處毋庸置疑,除了美化了咱們的API以外,也方便了前端同窗的數據處理。前端
咱們將統一的返回模型放在.ToolKits
層中,以前說過這裏主要是公共的工具類、擴展方法。git
新建一個Base文件夾,添加響應實體類ServiceResult.cs
,在Enum文件夾下單獨定義一個ServiceResultCode
響應碼枚舉,0/1。分別表明 成功和失敗。github
//ServiceResultCode.cs namespace Meowv.Blog.ToolKits.Base.Enum { /// <summary> /// 服務層響應碼枚舉 /// </summary> public enum ServiceResultCode { /// <summary> /// 成功 /// </summary> Succeed = 0, /// <summary> /// 失敗 /// </summary> Failed = 1, } }
//ServiceResult.cs using Meowv.Blog.ToolKits.Base.Enum; using System; namespace Meowv.Blog.ToolKits.Base { /// <summary> /// 服務層響應實體 /// </summary> public class ServiceResult { /// <summary> /// 響應碼 /// </summary> public ServiceResultCode Code { get; set; } /// <summary> /// 響應信息 /// </summary> public string Message { get; set; } /// <summary> /// 成功 /// </summary> public bool Success => Code == ServiceResultCode.Succeed; /// <summary> /// 時間戳(毫秒) /// </summary> public long Timestamp { get; } = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); /// <summary> /// 響應成功 /// </summary> /// <param name="message"></param> /// <param name="data"></param> /// <returns></returns> public void IsSuccess(string message = "") { Message = message; Code = ServiceResultCode.Succeed; } /// <summary> /// 響應失敗 /// </summary> /// <param name="message"></param> /// <param name="data"></param> /// <returns></returns> public void IsFailed(string message = "") { Message = message; Code = ServiceResultCode.Failed; } /// <summary> /// 響應失敗 /// </summary> /// <param name="exexception></param> /// <param name="data"></param> /// <returns></returns> public void IsFailed(Exception exception) { Message = exception.InnerException?.StackTrace; Code = ServiceResultCode.Failed; } } }
能夠看到,還定義了 string 類型的 Message,bool 類型的 Success,Success取決於Code == ServiceResultCode.Succeed
的結果。還有一個當前的時間戳Timestamp。api
其中還有IsSuccess(...)
和IsFailed(...)
方法,當咱們成功返回數據或者當系統出錯或者參數異常的時候執行,這一點也不難理解吧。異步
這個返回模型暫時只支持無需返回參數的api使用,還須要擴展一下,當咱們須要返回其它各類複雜類型的數據就行不通了。因此還須要添加一個支持泛型的返回模型,新建模型類:ServiceResultOfT.cs
,這裏的T就是咱們的返回結果,而後繼承咱們的ServiceResult,指定T爲class。並從新編寫一個IsSuccess(...)
方法,代碼以下:async
//ServiceResultOfT.cs using Meowv.Blog.ToolKits.Base.Enum; namespace Meowv.Blog.ToolKits.Base { /// <summary> /// 服務層響應實體(泛型) /// </summary> /// <typeparam name="T"></typeparam> public class ServiceResult<T> : ServiceResult where T : class { /// <summary> /// 返回結果 /// </summary> public T Result { get; set; } /// <summary> /// 響應成功 /// </summary> /// <param name="result"></param> /// <param name="message"></param> public void IsSuccess(T result = null, string message = "") { Message = message; Code = ServiceResultCode.Succeed; Result = result; } } }
此時針對無需返回參數和須要返回參數的api均可以知足要求了。可是還有一種就沒辦法了,那就是帶分頁的數據,咱們都應該知道想要分頁,數據總數確定是必不可少的。函數
因此此時還須要擴展一個分頁的響應實體,當咱們使用的時候,直接將分頁響應實體做爲上面寫的ServiceResult<T>
中的T參數,便可知足需求。工具
新建文件夾Paged,添加總數接口IHasTotalCount
、返回結果列表接口IListResult
post
//IHasTotalCount.cs namespace Meowv.Blog.ToolKits.Base.Paged { public interface IHasTotalCount { /// <summary> /// 總數 /// </summary> int Total { get; set; } } } //IListResult.cs using System.Collections.Generic; namespace Meowv.Blog.ToolKits.Base.Paged { public interface IListResult<T> { /// <summary> /// 返回結果 /// </summary> IReadOnlyList<T> Item { get; set; } } }
IListResult<T>
接受一個參數,並將其指定爲IReadOnlyList
返回。
如今來實現IListResult
接口,新建ListResult
實現類,繼承IListResult
,在構造函數中爲其賦值,代碼以下:
//ListResult.cs using System.Collections.Generic; namespace Meowv.Blog.ToolKits.Base.Paged { public class ListResult<T> : IListResult<T> { IReadOnlyList<T> item; public IReadOnlyList<T> Item { get => item ?? (item = new List<T>()); set => item = value; } public ListResult() { } public ListResult(IReadOnlyList<T> item) { Item = item; } } }
最後新建咱們的分頁響應實體接口:IPagedList
和分頁響應實體實現類:PagedList
,它同時也要接受一個泛型參數 T。
接口繼承了IListResult<T>
和IHasTotalCount
,實現類繼承ListResult<T>
和IPagedList<T>
,在構造函數中爲其賦值。代碼以下:
//IPagedList.cs namespace Meowv.Blog.ToolKits.Base.Paged { public interface IPagedList<T> : IListResult<T>, IHasTotalCount { } } //PagedList.cs using Meowv.Blog.ToolKits.Base.Paged; using System.Collections.Generic; namespace Meowv.Blog.ToolKits.Base { /// <summary> /// 分頁響應實體 /// </summary> /// <typeparam name="T"></typeparam> public class PagedList<T> : ListResult<T>, IPagedList<T> { /// <summary> /// 總數 /// </summary> public int Total { get; set; } public PagedList() { } public PagedList(int total, IReadOnlyList<T> result) : base(result) { Total = total; } } }
到這裏咱們的返回模型就圓滿了,看一下此時下咱們的項目層級目錄。
接下來去實踐一下,修改咱們以前建立的增刪改查接口的返回參數。
//IBlogService.cs using Meowv.Blog.Application.Contracts.Blog; using Meowv.Blog.ToolKits.Base; using System.Threading.Tasks; namespace Meowv.Blog.Application.Blog { public interface IBlogService { //Task<bool> InsertPostAsync(PostDto dto); Task<ServiceResult<string>> InsertPostAsync(PostDto dto); //Task<bool> DeletePostAsync(int id); Task<ServiceResult> DeletePostAsync(int id); //Task<bool> UpdatePostAsync(int id, PostDto dto); Task<ServiceResult<string>> UpdatePostAsync(int id, PostDto dto); //Task<PostDto> GetPostAsync(int id); Task<ServiceResult<PostDto>> GetPostAsync(int id); } }
接口所有爲異步方式,用ServiceResult
包裹做爲返回模型,添加和更新T參數爲string類型,刪除就直接不返回結果,而後查詢爲:ServiceResult<PostDto>
,再看一下實現類:
//BlogService.cs ... public async Task<ServiceResult<string>> InsertPostAsync(PostDto dto) { var result = new ServiceResult<string>(); var entity = new Post { Title = dto.Title, Author = dto.Author, Url = dto.Url, Html = dto.Html, Markdown = dto.Markdown, CategoryId = dto.CategoryId, CreationTime = dto.CreationTime }; var post = await _postRepository.InsertAsync(entity); if (post == null) { result.IsFailed("添加失敗"); return result; } result.IsSuccess("添加成功"); return result; } public async Task<ServiceResult> DeletePostAsync(int id) { var result = new ServiceResult(); await _postRepository.DeleteAsync(id); return result; } public async Task<ServiceResult<string>> UpdatePostAsync(int id, PostDto dto) { var result = new ServiceResult<string>(); var post = await _postRepository.GetAsync(id); if (post == null) { result.IsFailed("文章不存在"); return result; } post.Title = dto.Title; post.Author = dto.Author; post.Url = dto.Url; post.Html = dto.Html; post.Markdown = dto.Markdown; post.CategoryId = dto.CategoryId; post.CreationTime = dto.CreationTime; await _postRepository.UpdateAsync(post); result.IsSuccess("更新成功"); return result; } public async Task<ServiceResult<PostDto>> GetPostAsync(int id) { var result = new ServiceResult<PostDto>(); var post = await _postRepository.GetAsync(id); if (post == null) { result.IsFailed("文章不存在"); return result; } var dto = new PostDto { Title = post.Title, Author = post.Author, Url = post.Url, Html = post.Html, Markdown = post.Markdown, CategoryId = post.CategoryId, CreationTime = post.CreationTime }; result.IsSuccess(dto); return result; } ...
當成功時,調用IsSuccess(...)
方法,當失敗時,調用IsFailed(...)
方法。最終咱們返回的是new ServiceResult()
或者new ServiceResult<T>()
對象。
同時不要忘記在Controller中也須要修改一下,以下:
//BlogController.cs ... ... public async Task<ServiceResult<string>> InsertPostAsync([FromBody] PostDto dto) ... ... public async Task<ServiceResult> DeletePostAsync([Required] int id) ... ... public async Task<ServiceResult<string>> UpdatePostAsync([Required] int id, [FromBody] PostDto dto) ... ... public async Task<ServiceResult<PostDto>> GetPostAsync([Required] int id) ... ...
此時再去咱們的Swagger文檔發起請求,這裏咱們調用一下查詢接口看看返回的樣子,看看效果吧。
本篇內容比較簡單,主要是包裝咱們的api,讓返回結果顯得比較正式一點。那麼,你學會了嗎?😁😁😁