API接口通讯参数规范
問題引出
通常在很多的公司里面,對于接口的返回值沒做太大規范,所以會比較??吹礁鱾€項目各自定義隨意的返回值,比如以下情況:
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接口通讯参数规范的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 同一局域网内_Pycharm访问服务器
- 下一篇: 一键发布部署vs插件[AntDeploy