基于 abp vNext 和 .NET Core 开发博客项目 - 统一规范API,包装返回模型

上一篇文章使用自定义仓储完成了简单的增删改查案例,有心的同学可以看出,我们的返回参数一塌糊涂,显得很不友好。

在实际开发过程中,每个公司可能不尽相同,但都大同小异,我们的返回数据都是包裹在一个公共的模型下面的,而不是直接返回最终数据,在返回参数中,显示出当前请求的时间戳,是否请求成功,如果错误那么错误的消息是什么,状态码(状态码可以是我们自己定义的值)等等。可能显得很繁琐,没必要,但这样做的好处毋庸置疑,除了美化了我们的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;}}
}

到这里我们的返回模型就圆满了,看一下此时下我们的项目层级目录。

接下来去实践一下,修改我们之前创建的增删改查接口的返回参数。

//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,让返回结果显得比较正式一点。那么,你学会了吗?????????????

开源地址:https://github.com/Meowv/Blog/tree/blog_tutorial

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309774.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

奔小康赚大钱 HDU - 2255( 二分图匹配KM算法详解)

题目 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革&#xff1a;重新分配房子。 这可是一件大事,关系到人民的住房问题啊。村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住&#xff08;如果有老百姓没房子住的话&#xff0c;容易引起不安定因素&…

记一次排查线上程序内存的忽高忽低,又是大集合惹祸了

一&#xff1a;背景1. 讲故事昨天继续还技术债&#xff0c;优化一轮后的程序拉到线上后内存继续忽高忽低&#xff0c;低的时候20G&#xff0c;高的时候30G&#xff0c;过了一会又下降了几个G&#xff0c;毫无疑问&#xff0c;程序中有什么集合或者什么操作占用了大量内存&#…

[C++11]forward完美转发

// 函数原型 template <class T> T&& forward (typename remove_reference<T>::type& t) noexcept; template <class T> T&& forward (typename remove_reference<T>::type&& t) noexcept;// 精简之后的样子 std::forward…

Pipe HDU - 2150(判断线段相交+向量叉乘线代详解)

题目&#xff1a; 经过激烈的争夺&#xff0c;Lele终于把那块地从Yueyue的手里抢了回来。接下来&#xff0c;Lele要开始建造他的灌溉系统。 通过咨询Lele的好友——化学系的TT&#xff0c;Lele决定在田里挖出N条沟渠&#xff0c;每条沟渠输送一种肥料。 每条沟渠可以看作是一…

win7如何将计算机移至桌面,如何将win7电脑桌面的文件转移到其他盘中?

想必很多朋友都和小编一样&#xff0c;是一个嫌麻烦的人&#xff0c;是一个不怎么爱收拾的人吧?这种人有一个通病&#xff0c;那就是喜欢将一些重要的文件放置在win7 64位纯净版下载的桌面上&#xff0c;这样的话&#xff0c;不仅容易找到&#xff0c;而且方便使用&#xff0c…

凸包算法知识总结

首先&#xff0c;什么是凸包&#xff1f; 假设平面上有p0~p12共13个点&#xff0c;过某些点作一个多边形&#xff0c;使这个多边形能把所有点都“包”起来。当这个多边形是凸多边形的时候&#xff0c;我们就叫它“凸包”。 处理何种问题&#xff1a;凸包可以看成在木板上钉许多…

[C++11]shared_ptr使用的注意事项(内存被重复析构,内存泄漏问题)

shared_ptr使用的注意事项: 1.不能使用一个原始地址初始化多个共享智能指针 2.函数不能返回管理了this的共享智能指针对象 3.共享智能指针不能循环引用 不能使用一个原始地址初始化多个共享智能指针 代码如下: #include <iostream> #include <memory> using name…

一文解读使用WinDbg排查iis 中CPU占用高的站点问题

一、概述在Window服务器部署程序后&#xff0c;可能因为代码的不合理或者其他各种各样的问题&#xff0c;会导致CPU暴增&#xff0c;甚至达到100%等情况&#xff0c;严重危及到服务器的稳定以及系统稳定&#xff0c;但是一般来说对于已发布的程序&#xff0c;没法即时看到出问题…

Power Network POJ - 1459(EK算法模板+详解)

题意&#xff1a; 总共有a个节点&#xff0c;其中有发电站b个、用户c个和调度器a-b-c个三种节点&#xff0c;每个发电站有一个最大发电量&#xff0c;每个用户有个最大接受电量&#xff0c;现在有d条有向边&#xff0c;边有一个最大的流量代表&#xff0c;最多可以流出这么多电…

[C++11]shared_ptr共享智能指针的初始化与使用

使用智能指针需要添加头文件: 代码如下: #include <memory>shared_ptr使用的注意事项: 1.不能使用一个原始地址初始化多个共享智能指针 2.函数不能返回管理了this的共享智能指针对象 3.共享智能指针不能循环引用 初始化: 1.通过构造函数初始化 代码如下: std::shar…

人在职场,表达似水

最近几天集中在和部门内的小伙伴做1&#xff1a;1Talk。发现有不少人表达能力真的不是很好&#xff0c;而且水平长期停滞不前。现场与他们分享了一些我的经验&#xff0c;趁热打铁也想在这里分享给大家。表达能力不好本身就是做技术的人身上的一个常见标签。如果仅仅是标签也就…

[C++11]弱引用智能指针weak_ptr初始化和相关的操作函数

弱引用智能指针 std::weak_ptr 可以看做是 shared_ptr 的助手&#xff0c;它不管理 shared_ptr 内部的指针。std::weak_ptr 没有重载操作符 * 和 ->&#xff0c;因为它不共享指针&#xff0c;不能操作资源&#xff0c;所以它的构造不会增加引用计数&#xff0c;析构也不会减…

电子科技大学计算机网络技术专业,计算机专业前景如何?最强十大高校有哪些?电子科大排名多少?...

近几年计算机专业以及与计算机相关的专业可谓是火的不能再火&#xff0c;越来越多的考生在高考时选择了计算机专业或者与计算机相关的专业&#xff01;事实也是如此&#xff0c;随着科技的进步&#xff0c;信息技术的迅猛发展&#xff0c;特别是计算机技术的发展&#xff0c;以…

写一个简版 asp.net core

动手写一个简版 asp.net coreIntro之前看到过蒋金楠老师的一篇 200 行代码带你了解 asp.net core 框架&#xff0c;最近参考蒋老师和 Edison 的文章和代码&#xff0c;结合自己对 asp.net core 的理解 &#xff0c;最近自己写了一个 MiniAspNetCore &#xff0c;写篇文章总结一…

[C++11]独占的智能指针unique_ptr的删除器

unique_ptr 指定删除器和 shared_ptr 指定删除器是有区别的&#xff0c;unique_ptr 指定删除器的时候需要确定删除器的类型&#xff0c;所以不能像 shared_ptr 那样直接指定删除器&#xff0c;举例说明&#xff1a; 代码如下: #include <iostream> #include <memory…

如何在Windows上使用Git创建一个可执行脚本?

长话短说&#xff0c;今天介绍如何在windows上使用Git上创建一个可执行的shell脚本。“首先我们要知道windows上Git默认添加的文件权限是:-rw-r--r--(对应权限值是644)&#xff0c;而通常创建的shell脚本都希望天然可执行&#xff0c;故有必要在Windows上使用Git管理shell脚本时…

[C++11]共享智能指针shared_ptr指定删除器

当智能指针管理的内存对应的引用计数变为 0 的时候&#xff0c;这块内存就会被智能指针析构掉了。另外&#xff0c;我们在初始化智能指针的时候也可以自己指定删除动作&#xff0c;这个删除操作对应的函数被称之为删除器&#xff0c;这个删除器函数本质是一个回调函数&#xff…

基于 abp vNext 和 .NET Core 开发博客项目 - 再说Swagger,分组、描述、小绿锁

在开始本篇正文之前&#xff0c;解决一个 疯疯过 指出的错误&#xff0c;再次感谢指正。步骤如下&#xff1a;删掉.Domain.Shared层中的项目引用&#xff0c;添加nuget依赖包Volo.Abp.Identity.Domain.Shared&#xff0c;可以使用命令&#xff1a;Install-Package Volo.Abp.Ide…

[C++11]独占的智能指针unique_ptr的初始化和使用

std::unique_ptr 是一个独占型的智能指针&#xff0c;它不允许其他的智能指针共享其内部的指针&#xff0c;可以通过它的构造函数初始化一个独占智能指针对象&#xff0c;但是不允许通过赋值将一个 unique_ptr 赋值给另一个 unique_ptr。std::unique_ptr 不允许复制&#xff0c…