API接口通讯参数规范
問題引出
通常在很多的公司里面,對(duì)于接口的返回值沒做太大規(guī)范,所以會(huì)比較常看到各個(gè)項(xiàng)目各自定義隨意的返回值,比如以下情況:
1. 直接返回bool值(True或者False)
2. 返回void,只要不是異常信息,默認(rèn)成功
3. 返回各種狀態(tài)碼
4.?返回多個(gè)值,還要使用 out 來添加返回參數(shù)
5. 。。。
對(duì)于項(xiàng)目數(shù)量稍微多點(diǎn)的公司來說,接手多個(gè)項(xiàng)目的同事估計(jì)要吐血,所以項(xiàng)目間的業(yè)務(wù)通信規(guī)范是很有必要的。
解決方案
結(jié)合個(gè)人項(xiàng)目經(jīng)驗(yàn),定義一個(gè)專門用來封裝返回值信息的通用類,如下:?
/// <summary>/// 返回結(jié)果/// </summary>public interface IResult{/// <summary>/// 結(jié)果狀態(tài)碼/// </summary>ResultCode Code { get; set; }/// <summary>/// 提示信息/// </summary>/// <example>操作成功</example>string Message { get; set; }/// <summary>/// 是否成功/// </summary>bool Success { get; }}/// <summary>/// 返回的附帶泛型數(shù)據(jù)/// </summary>public interface IResult<TType> : IResult{/// <summary>/// 返回的附帶數(shù)據(jù)/// </summary>TType Data { get; set; }}? 這個(gè)ResultCode是針對(duì)業(yè)務(wù)操作結(jié)果的自定義枚舉,用來標(biāo)志當(dāng)前返回的一個(gè)業(yè)務(wù)結(jié)果
public enum ResultCode{/// <summary>/// 操作成功///</summary>[Display(Name = "操作成功")]Ok = 1,/// <summary>/// 操作失敗///</summary>[Display(Name = "操作失敗")]Fail = 11,/// <summary>/// 登陸失敗///</summary>[Display(Name = "登陸失敗")]LoginFail = 12,/// <summary>/// 沒有該數(shù)據(jù)///</summary>[Display(Name = "沒有數(shù)據(jù)")]NoRecord = 13,/// <summary>/// 用戶不存在///</summary>[Display(Name = "用戶不存在")]NoSuchUser = 14,/// <summary>/// 未登錄///</summary>[Display(Name = "未登錄")]Unauthorized = 20,/// <summary>/// 未授權(quán)/// </summary>[Display(Name = "未授權(quán)")]Forbidden = 21,/// <summary>/// 無效Token/// </summary>[Display(Name = "無效Token")]InvalidToken = 22,/// <summary>/// 參數(shù)驗(yàn)證失敗/// </summary>[Display(Name = "參數(shù)驗(yàn)證失敗")]InvalidData = 23,/// <summary>/// 無效用戶/// </summary>[Display(Name = "無效用戶")]InvalidUser = 24}? 有了以上的接口,我們可以看一下具體實(shí)現(xiàn)
public class Result : IResult{private string _message;/// <summary>/// 是否成功/// </summary>public bool Success => Code == ResultCode.Ok;/// <summary>/// 結(jié)果碼/// </summary>public ResultCode Code {get; set;}/// <summary>/// 提示信息/// </summary>public string Message{get { return _message ?? Code.DisplayName(); }set { _message = value; }}/// <summary>/// 返回結(jié)果,默認(rèn)成功/// </summary>public Result(){Code = ResultCode.Ok;}/// <summary>/// 返回結(jié)果/// </summary>/// <param name="code">狀態(tài)碼</param>/// <param name="message">提示信息</param>public Result(ResultCode code, string message = null){Code = code;Message = message;} }這里我們定義了實(shí)現(xiàn)類,注意默認(rèn)的構(gòu)造函數(shù)是返回成功的,這方便我們后面針對(duì)業(yè)務(wù)對(duì)這個(gè)返回結(jié)果再次進(jìn)行擴(kuò)展。細(xì)心的大家應(yīng)該注意到了返回的提示信息,我們針對(duì)上面的自定義枚舉的提示信息會(huì)進(jìn)行顯示,后面具體實(shí)現(xiàn)再看。先看一下我們的泛型返回結(jié)果的實(shí)現(xiàn)
/// <summary>/// 返回結(jié)果/// </summary>public class Result<TType> : Result, IResult<TType>{/// <summary>/// cotr/// </summary>public Result(){}/// <summary>/// 返回結(jié)果/// </summary>public Result(TType data): base(ResultCode.Ok){Data = data;}/// <summary>/// 返回結(jié)果/// </summary>/// <param name="code">狀態(tài)碼</param>/// <param name="message">提示信息</param>public Result(ResultCode code, string message = null): base(code, message){}/// <summary>/// 返回結(jié)果/// </summary>public Result(ResultCode code, string message = null, TType data = default(TType)): base(code, message){Data = data;}/// <summary>/// 返回業(yè)務(wù)數(shù)據(jù)/// </summary>public TType Data { get; set; }}好有了這些,我們?cè)赗esult類中定義一些靜態(tài)方法對(duì)結(jié)果進(jìn)行封裝,這樣可以讓我們?cè)跇I(yè)務(wù)層進(jìn)行快速的調(diào)用
/// <summary>/// 返回指定 Code/// </summary>public static Result FromCode(ResultCode code, string message = null){return new Result(code, message);}/// <summary>/// 返回錯(cuò)誤信息/// </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>/// 返回錯(cuò)誤信息/// </summary>public static Result<T> FromError<T>(string message, ResultCode code = ResultCode.Fail){return new Result<T>(code, message);}/// <summary>/// 返回?cái)?shù)據(jù)/// </summary>public static Result<T> FromData<T>(T data){return new Result<T>(data);}/// <summary>/// 返回?cái)?shù)據(jù)和提示信息/// </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);}好了有了上面這些,我們?cè)撊绾握{(diào)用呢?當(dāng)我們需要直接返回成功時(shí),我們可以這樣
return Result.Ok();前端接收到的結(jié)果如下:
當(dāng)我們需要返回帶有數(shù)據(jù)的結(jié)果時(shí),我們可以這樣:
var list = new List<string>{"lex1","lex2"};return Result.FromData(list);? 前端接收到的結(jié)果如下:
當(dāng)我們需要返回指定Code的時(shí)候,如下:
return Result.FromCode(ResultCode.LoginFail);? 前端接收到的結(jié)果如下:
? 我們可以看到上面的提示信息是我們?cè)诿杜e上定義的信息,這是我們?cè)赗esult類中對(duì)Message進(jìn)行了Code.DisplayName(),思想很簡(jiǎn)單,就是對(duì)枚舉進(jìn)行了擴(kuò)展,利用DisplayAttribute的公用方法顯示信息,那我們?cè)趺粗朗裁磿r(shí)候調(diào)用DisplayAttribute的合適方法呢?
我們先定義一個(gè)類DisplayProperty,用來對(duì)應(yīng)DisplayAttribute的各個(gè)屬性
public enum DisplayProperty{/// <summary>/// 名稱/// </summary> Name,/// <summary>/// 短名稱/// </summary> ShortName,/// <summary>/// 分組名稱/// </summary> GroupName,/// <summary>/// 說明/// </summary> Description,/// <summary>/// 排序/// </summary> Order,/// <summary>/// 水印信息/// </summary> Prompt,}有了這個(gè)之后,我們的枚舉擴(kuò)展方法如下:
/// <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>/// 獲取枚舉指定的顯示內(nèi)容/// </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的擴(kuò)展,方法在下面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;}
再看針對(duì)MemberInfo的一個(gè)擴(kuò)展,這里面就根據(jù)我們傳入的DisplayProperty屬性值調(diào)用了DisplayAttribute的對(duì)應(yīng)方法
/// <summary>/// 獲取枚舉指定的顯示內(nèi)容/// </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;}到此我們的這個(gè)業(yè)務(wù)通訊結(jié)果已經(jīng)可以了,再細(xì)想,有幾個(gè)問題需要我們解決的:
1.?ResultCode的意義?
2. 公司這么多項(xiàng)目都這樣的話,如果某個(gè)系統(tǒng)需要新增一個(gè)提示或者英文不規(guī)范修改了,那會(huì)不會(huì)造成不一致呢?
?
后續(xù)文章會(huì)針對(duì)這些問題和可能存在的問題進(jìn)行探討!
轉(zhuǎn)載于:https://www.cnblogs.com/lex-wu/p/10370402.html
總結(jié)
以上是生活随笔為你收集整理的API接口通讯参数规范的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 同一局域网内_Pycharm访问服务器
- 下一篇: 一键发布部署vs插件[AntDeploy