问题引出  

  通常在很多的公司里面,对于接口的返回值没做太大规范,所以会比较常看到各个项目各自定义随意的返回值,比如以下情况:

  1. 直接返回bool值(True或者False)

  2. 返回void,只要不是异常信息,默认成功

  3. 返回各种状态码

  4. 返回多个值,还要使用 out 来添加返回参数

  5. 。。。

  对于项目数量稍微多点的公司来说,接手多个项目的同事估计要吐血,所以项目间的业务通信规范是很有必要的。

  解决方案

  结合个人项目经验,定义一个专门用来封装返回值信息的通用类,如下:   

    /// <summary>/// 返回结果/// </summary>public interface IResult{/// <summary>/// 结果状态码/// </summary>ResultCode Code { get; set; }/// <summary>/// 提示信息/// </summary>/// <example>操作成功</example>string Message { get; set; }/// <summary>/// 是否成功/// </summary>bool Success { get; }}/// <summary>/// 返回的附带泛型数据/// </summary>public interface IResult<TType> : IResult{/// <summary>/// 返回的附带数据/// </summary>TType Data { get; set; }}

  这个ResultCode是针对业务操作结果的自定义枚举,用来标志当前返回的一个业务结果

 public enum ResultCode{/// <summary>/// 操作成功///</summary>[Display(Name = "操作成功")]Ok = 1,/// <summary>/// 操作失败///</summary>[Display(Name = "操作失败")]Fail = 11,/// <summary>/// 登陆失败///</summary>[Display(Name = "登陆失败")]LoginFail = 12,/// <summary>/// 没有该数据///</summary>[Display(Name = "没有数据")]NoRecord = 13,/// <summary>/// 用户不存在///</summary>[Display(Name = "用户不存在")]NoSuchUser = 14,/// <summary>/// 未登录///</summary>[Display(Name = "未登录")]Unauthorized = 20,/// <summary>/// 未授权/// </summary>[Display(Name = "未授权")]Forbidden = 21,/// <summary>/// 无效Token/// </summary>[Display(Name = "无效Token")]InvalidToken = 22,/// <summary>/// 参数验证失败/// </summary>[Display(Name = "参数验证失败")]InvalidData = 23,/// <summary>/// 无效用户/// </summary>[Display(Name = "无效用户")]InvalidUser = 24}

  有了以上的接口,我们可以看一下具体实现  

public class Result : IResult{private string _message;/// <summary>/// 是否成功/// </summary>public bool Success => Code == ResultCode.Ok;/// <summary>/// 结果码/// </summary>public ResultCode Code {get; set;}/// <summary>/// 提示信息/// </summary>public string Message{get { return _message ?? Code.DisplayName(); }set { _message = value; }}/// <summary>/// 返回结果,默认成功/// </summary>public Result(){Code = ResultCode.Ok;}/// <summary>/// 返回结果/// </summary>/// <param name="code">状态码</param>/// <param name="message">提示信息</param>public Result(ResultCode code, string message = null){Code = code;Message = message;}        }    

  这里我们定义了实现类,注意默认的构造函数是返回成功的,这方便我们后面针对业务对这个返回结果再次进行扩展。细心的大家应该注意到了返回的提示信息,我们针对上面的自定义枚举的提示信息会进行显示,后面具体实现再看。先看一下我们的泛型返回结果的实现

    /// <summary>/// 返回结果/// </summary>public class Result<TType> : Result, IResult<TType>{/// <summary>/// cotr/// </summary>public Result(){}/// <summary>/// 返回结果/// </summary>public Result(TType data): base(ResultCode.Ok){Data = data;}/// <summary>/// 返回结果/// </summary>/// <param name="code">状态码</param>/// <param name="message">提示信息</param>public Result(ResultCode code, string message = null): base(code, message){}/// <summary>/// 返回结果/// </summary>public Result(ResultCode code, string message = null, TType data = default(TType)): base(code, message){Data = data;}/// <summary>/// 返回业务数据/// </summary>public TType Data { get; set; }}

  好有了这些,我们在Result类中定义一些静态方法对结果进行封装,这样可以让我们在业务层进行快速的调用

        /// <summary>/// 返回指定 Code/// </summary>public static Result FromCode(ResultCode code, string message = null){return new Result(code, message);}/// <summary>/// 返回错误信息/// </summary>public static Result FromError(string message, ResultCode code = ResultCode.Fail){return new Result(code, message);}/// <summary>/// 返回成功/// </summary>public static Result Ok(string message = null){return FromCode(ResultCode.Ok, message);}/// <summary>/// 返回指定 Code/// </summary>public static Result<T> FromCode<T>(ResultCode code, string message = null){return new Result<T>(code, message);}/// <summary>/// 返回指定 Code和提示信息/// </summary>public static Result<T> FromCode<T>(ResultCode code, T data, string message = null){return new Result<T>(code, message, data);}       /// <summary>/// 返回错误信息/// </summary>public static Result<T> FromError<T>(string message, ResultCode code = ResultCode.Fail){return new Result<T>(code, message);}/// <summary>/// 返回数据/// </summary>public static Result<T> FromData<T>(T data){return new Result<T>(data);}/// <summary>/// 返回数据和提示信息/// </summary>public static Result<T> FromData<T>(T data, string message){return new Result<T>(ResultCode.Ok, message, data);}/// <summary>/// 返回成功/// </summary>public static Result<T> Ok<T>(T data){return FromData(data);}

  好了有了上面这些,我们该如何调用呢?当我们需要直接返回成功时,我们可以这样  

return Result.Ok();

  前端接收到的结果如下:

  当我们需要返回带有数据的结果时,我们可以这样:

    var list = new List<string>{"lex1","lex2"};return Result.FromData(list);

  前端接收到的结果如下:

  当我们需要返回指定Code的时候,如下:

return Result.FromCode(ResultCode.LoginFail);

  前端接收到的结果如下:

  我们可以看到上面的提示信息是我们在枚举上定义的信息,这是我们在Result类中对Message进行了Code.DisplayName(),思想很简单,就是对枚举进行了扩展,利用DisplayAttribute的公用方法显示信息,那我们怎么知道什么时候调用DisplayAttribute的合适方法呢?

  我们先定义一个类DisplayProperty,用来对应DisplayAttribute的各个属性

    public enum DisplayProperty{/// <summary>/// 名称/// </summary>
        Name,/// <summary>/// 短名称/// </summary>
        ShortName,/// <summary>/// 分组名称/// </summary>
        GroupName,/// <summary>/// 说明/// </summary>
        Description,/// <summary>/// 排序/// </summary>
        Order,/// <summary>/// 水印信息/// </summary>
        Prompt,}

  有了这个之后,我们的枚举扩展方法如下:

        /// <summary>/// 获取枚举说明/// </summary>public static string DisplayName(this Enum val){return val.Display(DisplayProperty.Name) as string;}/// <summary>/// 获取枚举短名称说明/// </summary>public static string DisplayShortName(this Enum val){return val.Display(DisplayProperty.ShortName) as string;}/// <summary>/// 获取枚举水印信息/// </summary>public static string DisplayPrompt(this Enum val){return val.Display(DisplayProperty.Prompt) as string;}/// <summary>/// 获取枚举备注/// </summary>public static string DisplayDescription(this Enum val){return val.Display(DisplayProperty.Description) as string;}    /// <summary>/// 获取枚举指定的显示内容/// </summary>public static object Display(this Enum val, DisplayProperty property){var enumType = val.GetType();var str = val.ToString();if (enumType.GetAttribute<FlagsAttribute>() != null && str.Contains(",")){var array = str.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim());var result = array.Aggregate("", (s, s1) =>{var f = enumType.GetField(s1);if (f != null){              //MethodInfo的扩展,方法在下面var text = f.Display(property);return s.IsNullOrEmpty() ? text.ToString() : $"{s},{text}";}return s;});return result.IsNullOrEmpty() ? null : result;}var field = enumType.GetField(str);if (field != null){return field.Display(property);}return null;}

  再看针对MemberInfo的一个扩展,这里面就根据我们传入的DisplayProperty属性值调用了DisplayAttribute的对应方法

        /// <summary>/// 获取枚举指定的显示内容/// </summary>public static object Display(this MemberInfo memberInfo, DisplayProperty property){if (memberInfo == null) return null;var display = memberInfo.GetAttribute<DisplayAttribute>();if (display != null){switch (property){case DisplayProperty.Name:return display.GetName();case DisplayProperty.ShortName:return display.GetShortName();case DisplayProperty.GroupName:return display.GetGroupName();case DisplayProperty.Description:return display.GetDescription();case DisplayProperty.Order:return display.GetOrder();case DisplayProperty.Prompt:return display.GetPrompt();}}return null;}

  到此我们的这个业务通讯结果已经可以了,再细想,有几个问题需要我们解决的:

  1. ResultCode的意义?

  2. 公司这么多项目都这样的话,如果某个系统需要新增一个提示或者英文不规范修改了,那会不会造成不一致呢?

  后续文章会针对这些问题和可能存在的问题进行探讨!

转载于:https://www.cnblogs.com/lex-wu/p/10370402.html

API接口通讯参数规范相关推荐

  1. API接口通讯参数规范(2)

    针对[API接口通讯参数规范]这篇文章留下的几个问题进行探讨. 问题1 试想一下,如果一个http请求返回一个500给我们,那我们是不是都不用看详情都知道该次请求发生了什么?这正是一个标准的结果码意义 ...

  2. Swagger3 API接口文档规范课程(Java1234)(内含教学视频+源代码)

    Swagger3 API接口文档规范课程(Java1234)(内含教学视频+源代码) 教学视频+源代码下载链接地址:https://download.csdn.net/download/weixin_ ...

  3. 算法API接口文档规范

    算法API接口文档规范 参考:百度AI开放平台:https://ai.baidu.com/ai-doc/FACE/yk37c1u4t 接口功能介绍 1.人脸比对:比对两张图片中人脸的相似度,并返回相似 ...

  4. 电商系统中API接口防止参数篡改和重放攻击(小程序/APP)

    说明:目前所有的系统架构都是采用前后端分离的系统架构,那么就不可能避免的需要服务对外提供API,那么如何保证对外的API的安全呢? 即生鲜电商中API接口防止参数篡改和重放攻击 目录 1. 什么是AP ...

  5. (转)API接口防止参数篡改和重放攻击

    API重放攻击(Replay Attacks)又称重播攻击.回放攻击.他的原理就是把之前窃听到的数据原封不动的重新发送给接收方.HTTPS并不能防止这种攻击,虽然传输的数据是经过加密的,窃听者无法得到 ...

  6. API接口防止参数被篡改和重放攻击

    1. 什么是API参数篡改? 说明:API参数篡改就是恶意人通过抓包的方式获取到请求的接口的参数,通过修改相关的参数,达到欺骗服务器的目的,常用的防止篡改的方式是用签名以及加密的方式.关注公众号码猿技 ...

  7. RESTful API接口文档规范小坑

    希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,谢谢关注. 前后端分离的开发模式,假如使用的是基于RESTful API的七层通讯协议,在联调的时候,如何避免配合过程中出现问 ...

  8. 如何写出安全的API接口(参数加密+超时处理+私钥验证+Https)

    上篇文章说到接口安全的设计思路,如果没有看到上篇博客,建议看完再来看这个. 通过园友们的讨论,以及我自己查了些资料,然后对接口安全做一个相对完善的总结,承诺给大家写个demo,今天一并放出. 对于安全 ...

  9. 如何写出安全的API接口(参数加密+超时处理+私钥验证+Https)- 续(附demo)

    转载:http://www.cnblogs.com/codeon/p/6123863.html 上篇文章说到接口安全的设计思路,如果没有看到上篇博客,建议看完再来看这个. 通过园友们的讨论,以及我自己 ...

最新文章

  1. php读取西门子plc_基于Socket访问西门子PLC系列教程(二)
  2. PHP 可能在未来十年内消失?
  3. iOS 进阶 第十四天(0416)
  4. 根据坐标点鼠标 不移动_CAD移动鼠标时,鼠标右下角有坐标提示,怎么取消?...
  5. 雅可比旋转求解对称二维矩阵的特征值和特征向量
  6. 自适应宽_移动端实现自适应缩放界面的方法汇总
  7. 监督学习 | 线性回归 之正则线性模型原理及Sklearn实现
  8. Python爬虫-- Scrapy框架
  9. Clear Type技术
  10. c#如何在FTP服务器上下载文件夹及子文件夹中的文件
  11. 谷歌google安装vue插件,(npm安装)避坑指南
  12. 小程序分账系统是什么?能解决二清吗?
  13. Flutter:文件与网络操作摘要
  14. 2022-2028全球多通道光纤旋转接头(FORJ)行业调研及趋势分析报告
  15. 太极计划——华夏民族软件腾飞的计划
  16. 查看eigen库版本的指令
  17. 原型模式——java实现原型模式的几种写法
  18. 【C++ Primer 学习笔记】: 容器和算法之【泛型算法】
  19. 论文笔记:Multi-level Alignment Network for Domain Adaptive Cross-modal Retrieval
  20. linux双显卡配置_Linux系统怎么配置双显卡

热门文章

  1. VSS自动发布站点功能扩展
  2. WSS2.0升级到WSS3.0
  3. LINUX-Shell第一课
  4. Angular面试从喜剧到悲剧的十个问题
  5. 2008R2文件服务器迁移到2012R2
  6. Android中mesure过程详解
  7. Cassandra 常见错误索引
  8. Unix信号处理一些笔记
  9. nginx+tomcat的负载均衡
  10. 网络存储导论第七章:重要系统灾备方法