基於 abp vNext 和 .NET Core 開發博客項目 - 統一規範API,包裝返回模型

来源:https://www.cnblogs.com/meowv/archive/2020/05/21/12924409.html
-Advertisement-
Play Games

上一篇文章(https://www.cnblogs.com/meowv/p/12916613.html)使用自定義倉儲完成了簡單的增刪改查案例,有心的同學可以看出,我們的返回參數一塌糊塗,顯得很不友好。 在實際開發過程中,每個公司可能不盡相同,但都大同小異,我們的返回數據都是包裹在一個公共的模型下麵 ...


上一篇文章(https://www.cnblogs.com/meowv/p/12916613.html)使用自定義倉儲完成了簡單的增刪改查案例,有心的同學可以看出,我們的返回參數一塌糊塗,顯得很不友好。

在實際開發過程中,每個公司可能不盡相同,但都大同小異,我們的返回數據都是包裹在一個公共的模型下麵的,而不是直接返回最終數據,在返回參數中,顯示出當前請求的時間戳,是否請求成功,如果錯誤那麼錯誤的消息是什麼,狀態碼(狀態碼可以是我們自己定義的值)等等。可能顯得很繁瑣,沒必要,但這樣做的好處毋庸置疑,除了美化了我們的API之外,也方便了前端同學的數據處理。

我們將統一的返回模型放在.ToolKits層中,之前說過這裡主要是公共的工具類、擴展方法。

新建一個Base文件夾,添加響應實體類ServiceResult.cs,在Enum文件夾下單獨定義一個ServiceResultCode響應碼枚舉,0/1。分別代表 成功和失敗。

//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; } = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;

        /// <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。

其中還有IsSuccess(...)IsFailed(...)方法,當我們成功返回數據或者當系統出錯或者參數異常的時候執行,這一點也不難理解吧。

這個返回模型暫時只支持無需返回參數的api使用,還需要擴展一下,當我們需要返回其它各種複雜類型的數據就行不通了。所以還需要添加一個支持泛型的返回模型,新建模型類:ServiceResultOfT.cs,這裡的T就是我們的返回結果,然後繼承我們的ServiceResult,指定T為class。並重新編寫一個IsSuccess(...)方法,代碼如下:

//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

//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;
        }
    }
}

到這裡我們的返回模型就圓滿了,看一下此時下我們的項目層級目錄。

1

接下來去實踐一下,修改我們之前創建的增刪改查介面的返回參數。

//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文檔發起請求,這裡我們調用一下查詢介面看看返回的樣子,看看效果吧。

2

本篇內容比較簡單,主要是包裝我們的api,讓返回結果顯得比較正式一點。那麼,你學會了嗎?

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在一個白板類應用的交互中一定會涉及到模式之間的更換和交互衝突。白板類軟體的交互模式一般包含了筆跡書寫模式,選擇模式,擦除筆跡模式等。多個模式之間存在切換,而切換可以發生在某個模式執行過程,如需要在白板軟體裡面支持筆跡書寫功能,在書寫的過程打斷進入筆跡的擦除模式。本文告訴大家我所在團隊的白板內核的模式... ...
  • 前言 預設的 index.html 顯示的 Loading 太簡陋了. 而且沒有載入進度條. 所以做了一個. 代碼地址 : https://github.com/BlazorPlus/BlazorDemoWasmLoading 只需要改 index. html <!DOCTYPE html> <ht ...
  • 近年來,企業管理軟體開發領域掀起了一陣快速開發平臺的風氣,很多人覺得這是開發界有人帶的節奏,很快就會消失了,軟體開發應該回歸最真實的代碼之中。 不過隨著時間的推移,越來越多的企業認識到快速開發平臺的重要性,相比於傳統的代碼式開發風格,快速開發平臺的優勢體現的淋漓盡致。 傳統開發平臺最突出的特點就是開 ...
  • 背景:我們的應用程式通常都是由多個程式集組成,例如一個 exe 程式依賴於多個 dll 程式集。在某些情況下,我們希望程式的分發能夠簡單,單獨一個 exe 就能正常運行。這種情況下,就需要將 dll 依賴項合併到 exe 主程式中。 本文章給大家講下非常好用的NuGet 包,Costura.Fody ...
  • 一、概述 在Window伺服器部署程式後,可能因為代碼的不合理或者其他各種各樣的問題,會導致CPU暴增,甚至達到100%等情況,嚴重危及到伺服器的穩定以及系統穩定,但是一般來說對於已發佈的程式,沒法即時看到出問題的代碼,而微軟提供了一個很好的工具“WinDbg”,使得我們能夠回溯問題。下麵講一下操作 ...
  • 前言, Blazor Assembly 需要最少 1.9M 的下載量. ( Blazor WebAssembly 船新項目下載量測試 , 僅供參考. ) 隨著程式越來越複雜, 引用的東西越來越多, 需要更多的下載量 , 有一些網站的網路可能較差, 載入這些文件需要一定的時間. 對於一些網站而言, 它 ...
  • 背景介紹:基於netcore2.2開發api介面程式,自定義了一個異常捕獲中間件,用於捕獲未經處理的異常以及狀態碼404、500等訪問(設計的出發點就是,出現了非200的響應,我這邊全部會進行處理成200,並返回固定格式的JSON格式數據),併進行統一的信息返回。 返回的JSON實體定義如下: 中間 ...
  • 前言: 昨天 Blazor WebAssembly 3.2 正式發佈了. 更新 VS2019後就能直接使用. 新建了兩個PWA項目, 一個不用asp.net core (靜態部署), 一個使用asp.net core (項目模板與伺服器交互) 其中下載量主要是預壓縮有沒有被使用 , 分別為 3.2M ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...