上一篇文章(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; } = 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。

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

基于 abp vNext 和 .NET Core 开发博客项目 - 统一规范API,包装返回模型的更多相关文章

  1. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(一)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  2. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(二)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  3. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(三)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  4. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(四)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  5. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(五)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  6. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(一)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  7. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(二)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  8. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(三)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  9. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(四)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

随机推荐

  1. 哈希Hash定义

    Hash,一般翻译做"散列”,也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值. ...

  2. 2019-2020-1 20199326《Linux内核原理与分析》第九周作业

    进程的切换和系统的一般执行过程 中断 中断在本质上都是软件或者硬件发生了某种情形而通知处理器的行为,处理器进而停止正在运行的指令流(当前进程),对这些通知做出相应反应,即转去执行预定义的中断处理程序( ...

  3. CSRF与平行越权的区别

    .CSRF攻击者不需要登录,越权攻击者也得登录,只是没有做针对性的控制: .CSRF攻击者自己不访问受攻击页面,诱导受害者在登录被攻击系统后点击攻击页面:越权攻击者可以直接访问受攻击页面: .CSRF ...

  4. 蚂蚁金服合作的RISE实验室到底有多牛?

    近日,蚂蚁金服与美国加州伯克利大学近期新成立的RISE实验室达成合作意向.RISE实验室的前身是著名伯克利AMP实验室,主导研发了当今大数据计算领域最前沿的开源系统:Apache Spark.Apac ...

  5. 人工智能与VR结合:带来体验多样性

    人工智能服务--微软认知服务(Microsoft Cognitive Services)最初包括视觉.语音.语言.知识和搜索五大类共 21 项 API.应用了这些 API 的系统能看.能听.能说话,并 ...

  6. C语言编程入门题目--No.12

    题目:判断101-200之间有多少个素数,并输出所有素数. 1.程序分析:判断素数的方法:用一个数分别去除2到sqrt(这个数),如果能被整除, 则表明此数不是素数,反之是素数. 2.程序源代码: # ...

  7. 网络流--最大流--POJ 1273 Drainage Ditches

    链接 Description Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clov ...

  8. JS对象与原型

    一. JS的对象 1.1 创建对象的几种方式 1.1.1 通过字面量创建对象 在js中,一对{} 其实就是一个对象 var person = { name: "tom", age: ...

  9. springboot中json转换LocalDateTime失败的bug解决过程

    环境:jdk1.8.maven.springboot 问题:前端通过json传了一个日期:date:2019-03-01(我限制不了前端开发给到后端的日期为固定格式,有些人就是这么不配合),      ...

  10. leetcode_雇佣 K 名工人的最低成本(优先级队列,堆排序)

    题干: 有 N 名工人. 第 i 名工人的工作质量为 quality[i] ,其最低期望工资为 wage[i] . 现在我们想雇佣 K 名工人组成一个工资组.在雇佣 一组 K 名工人时,我们必须按照下 ...