package com.diandaxia.common.utils; import java.util.Date; /** * by liyuxin 2019.12.16 更新 * 统一返回格式 */ public class ApiResponse<T> { /** * 此次HTTP请求任务的最终结果状态,即HTTP协议层面成功了,然后对应的业务层面的操作也成功了,完全成功。 * 举例:若HTTP请求层面没有任何异常抛出,业务层面也完全成功了,则flag=true; * 业务层面的完全成功的意思为:用户名和密码完全正确,后续所有的操作完全正确没有问题。 * 若用户名或密码错误那么就是业务层面的错误,若业务层面的错误则flag=false, * 重要:这是一个所有环节均全部成功的标记,方便前端使用。前端可以判断此标记,来判断请求的所有环节是否完全成功。 */ private Boolean flag; /** * 错误代码--标识。 * 这个code的作用,仅仅是来标识出错的时候的错误代码。没有任何含义,并非网上说的HTTP协议状态码。 * 服务端已经做了全局异常处理,服务端返回的协议状态码永远是200;不要再加status字段,毫无意义。 * 这个仅仅是一个标识,没有任何意思。 * 0:标识成功 * 1:标识一般的业务错误 * 2:标识一般的异常错误 * 3.标识token过期,当然为了方便前端使用,boolean类型的tkexp也标识token是否过期 * 4.带hurl帮助信息的错误 * 5.外部的API调用返回错误标识,External Error,举例:调用淘宝的API返回了错误 * 一般0,1,2,3,4,5就完全足够了;宗旨不是给用户友好的提示错误,宗旨是不给用户任何提示错误,直接默认全部帮他处理好。所以错误不细分。而是疯狂减少错误的几率。 * 若依然有一些,其它的错误需真的需要特殊标识,方便前端使用的需要向我报备,我会加在一个枚举bean里; */ private Integer code; /** * 错误详细信息标识; * 成功时为null;失败才需要理由。 */ private String msg; /** * 若成功往往有data数据 * 示例: * UserBase userBase = new UserBase(); * userBase.setUserName("李昱鑫"); * userBase.setUserPwd("123"); * userBase.setUserAge(100); * return ResponseUtil.okWithData(userBase); 对应结果1: * return ResponseUtil.okWithData(objectMapper.writeValueAsString(userBase)); 对应结果2: * * 结果1:{"flag":true,"code":0,"msg":"ok","data":{"userName":"李昱鑫","userPwd":"123","userAge":100},"tkexp":false,"hurl":null,"time":"2019-12-31 08:19:25"} * 结果2:{"flag":true,"code":0,"msg":"ok","data":"{\"userName\":\"李昱鑫\",\"userPwd\":\"123\",\"userAge\":100}","tkexp":false,"hurl":null,"time":"2019-12-31 08:21:40"} * 这个为Object不再为String,若为String的话,当数据量非常多的时候,会产生很多转义字符\,浪费网络流量,所以这里修改为object类型,其余的工作交给前端。 * 使用方法要求如下: * 1.当是用到第三方的接口时的返回时,比如淘宝的API接口,返回值必须与文档上一致。那么需要传入字符串,淘宝的举例:return ResponseUtil.okWithData(rsp.getBody()); * 虽然这样会产生很多的转义字符,但是没有办法,必须与文档的返回一致,这时data要传入String类型。 * 2.当是我们自己调用我们自己的接口时,就没有必须传入String了,直接传入Object就可以了。举例:查询数据库中的记录,直接返回mybatis的mapper查询的结果list对象就可以,举例: * Mapper.java里 -- List<JdpTbTrade> selectListByTids(String tids); Controller里 -- return ResponseUtil.okWithData(jdpTbTradeMapper.selectListByTids(tids)); * 这样就减少了转义字符,前端调试看着也方便。 */ private T data; /** * token过期的标记,这个比较常用,因为各个子项目都依赖token,当token过期的时候需要自动重新登录,获取新的token * 故把这个token过期单独出来标志,此标志仅仅表示token是否过期;秘钥错误等问题,并不标识 * tkexp为token_expire缩写。true 标识过期了 false标识正常,没有过期. */ private Boolean tkexp; /** * 当遇到错误的时候,有一些特殊错误必须要给出一套解决方案 来指导用户按流程操作,可以利用这个help_url,客户端要 * 提示用户打开这个链接进行查看相关教程,这个必须是一个url链接. */ private String hurl; /** * 外部的业务错误标识,External Error 缩写:exterr 举例:调用淘宝的API,分2种错误: * 1.若是我们自己的业务异常,则报错,举例token的验证,以及从Redis里取accessToken 都有可能异常, * 这种错误是我们自己的业务错误, * * 2.若我们自己的业务无错误,调用淘宝的接口,由于其他原因淘宝会给返回错误,这类错误是外部错误,需要特殊标识一下 * 方便客户端知道是外部的业务错误还是内部的业务错误。 * ----------------------------------------------------- * 以下是客户端需要做的: * 1.根据flag就知道是遇到错误了,还是完全正确了。 * * 2.若是遇到错误了,再通过额外标识这个字段,来判断是平台的接口返回的错误,还是我们自己的内部业务错误。 * * a.若是平台返回的错误【msg 里会是一个平台返回的完整 json字符串】,那么需要客户端解析msg里的字符串,然后做提示或其它默认处理操作。 * * b.若是我们自己的业务错误【msg 里会是一个普通的字符串 非json】,比如token过期,那么就不需要解析msg里的字符串,直接提示这个字符串就行。 * ---------------------------------------------------- * 第一种情况,我们自己的业务错误,内部错误。 * * { * "flag": false, * "code": 3, * "msg": "token expire please login again", * "data": null, * "tkexp": true, * "hurl": null, * "exterr": false, * "time": "2020-01-12 16:41:24" * } * * * 第二中情况,淘宝的API 返回错误。 * * { * "flag": false, * "code": 1, * "msg": "{\"error_response\":{\"code\":27,\"msg\":\"Invalid session\",\"sub_code\":\"invalid-sessionkey\",\"sub_msg\":\"非法或过期的SessionKey参数,请使用有效的SessionKey参数\",\"request_id\":\"556il21uswlw\"}}", * "data": null, * "tkexp": false, * "hurl": null, * "exterr": true, * "time": "2020-01-12 16:43:28" * } */ private Boolean exterr; /** * 响应处理时间,方便以后根据报文的时间来判断错误,spring boot 项目不要忘记配置jackson * spring.jackson.time-zone=GMT+8 * spring.jackson.date-format=yyyy-MM-dd HH:mm:ss */ private Date time; /** *用于内部构造 */ private ApiResponse(Boolean flag, Integer code, String msg, T data, Boolean tkexp, String hurl, Boolean exterr){ this.flag = flag; this.code = code; this.msg = msg; this.data = data; this.tkexp = tkexp; this.hurl = hurl; this.exterr = exterr; this.time = new Date(); } /** * Http请求全程无异常--->业务代码层面也成功,即完全响应成功,不带返回数据(这个也常用,msg加了一个success) */ public static <T> ApiResponse<T> ok(){ return new ApiResponse<T>(true, 0,"ok",null, false, null, false); } /** * Http请求全程无异常--->业务代码层面也成功,即完全响应成功,带返回数据(这个常用) */ public static <T> ApiResponse<T> okWithData(T data){ return new ApiResponse<T>(true, 0,"ok", data, false, null, false); } /** * Http请求全程无异常--->业务代码层面不成功(比如用户需要输入密码或缺少参数),即不能完全响应成功。算失败。 * 这个最常用,错误代码默认为1,一般性业务错误。 */ public static <T> ApiResponse<T> error(String msg){ return new ApiResponse<T>(false, 1, msg,null, false, null, false); } /** * Http请求有异常抛出--->走不到业务代码层面,即不能完全响应成功。 * 这个方法不常用,主要放在 SpringBoot项目的 全局异常类里使用。 * 把异常的信息传递进来.错误代码为2:标识一般的异常错误 */ public static <T> ApiResponse<T> errorException(String msg){ return new ApiResponse<T>(false, 2, msg,null, false, null, false); } /** * token过期,单独出来供全局异常处理那里使用,要求每一个子项目,必须有一个全局异常处理类,且这个全局异常处理类里必须包含 * 处理token过期的方法,错误代码为3.标识token过期,当然为了方便前端使用,boolean类型的tkexp也标识token是否过期 */ public static <T> ApiResponse<T> errorTokenExpire(){ return new ApiResponse<T>(false, 3, "Authorization token is expired",null, true, null, false); } /** * 有一些特殊错误必须要给出一套解决方案 来指导用户按流程操作,可以利用这个help_url * 提示用户打开这个链接进行查看相关教程,这个是必须返回一个教程对应的URL * 错误代码默认为1,一般性业务错误。 */ public static <T> ApiResponse<T> errorWithHelp(String msg, String hurl){ return new ApiResponse<T>(false, 4, msg,null, false, hurl, false); } /** * 调用各个电商平台的Api返回的错误,并非我们自身的业务错误,需要标识一下外部错误. */ public static <T> ApiResponse<T> errorExternalApi(String msg){ return new ApiResponse<T>(false, 5, msg,null, false, null, true); } /** * 【这个比较少用,只有特殊情况下,才会使用,而且要尽量少用,只有当需要前端根据code来做出【自动】反应时才会用到】 * Http请求全程无异常--->业务代码层面不成功,但需要特殊标识一下code,方便前端判断code自动做出处理。这个方法最不常用,特殊标识的 * 我的目标不是友好的提示错误,还是自动消掉错误,这个类里要尽可能少错误的定义. * 特殊情况下,某一些错误,需要特殊标识,方便前端判断 然后自动做出处理 * 现实中实际场景发现,每一个接口返回的code的意义可能都不同,无法统一code; * 举例:abc.diandaxia.com/aaa 接口若返回 code=100 可能代表的意思是 哎哎哎; * abc.diandaxia.com/bbb 接口也返回 code=100 他代码的意思是 哈哈哈; * 可见不同的接口会返回相同的code,代表不同的意思,具体意思由接口人自己规定了。 * 但是有几个code是共有code,不可私自定义,比如0-5;这几个code不可私自定义使用。 * @param code 不可以为0,1,2,3,4,5 */ public static <T> ApiResponse<T> errorOtherMark(Integer code, String msg){ if(code.equals(0) || code.equals(1) || code.equals(2) || code.equals(3) || code.equals(4) || code.equals(5)){ return new ApiResponse<T>(false, code, "code limit error",null, false, null, false); } return new ApiResponse<T>(false, code, msg,null, false, null, false); } /** * 【这个尽量少用,只有在一些特殊的情况下才使用】 * 场景1:一个接口需要返回临时token,但即使返回临时token 整理登录流程也算失败, * 所以最终会是:{flag:false,code:123,msg:需要短信二次认证,data:tempToken}, * 这样有中文提示,同时也带回了一个辅助数据,方便客户端做一些自动操作。 * 思维上,既然有okWithData 就也应该有 errorWithData,但是errorWithData * 仅仅只能一些特殊的情况下,才允许使用,所以命名为:errorOtherMarkWithData * 其它的说明与errorOtherMark一致。 * @param code 不可以为0,1,2,3,4,5 */ public static <T> ApiResponse<T> errorOtherMarkWithData(Integer code, String msg, T data){ if(code.equals(0) || code.equals(1) || code.equals(2) || code.equals(3) || code.equals(4) || code.equals(5)){ return new ApiResponse<T>(false, code, "code limit error",null, false, null, false); } return new ApiResponse<T>(false, code, msg, data, false, null, false); } //============================== 以下是setter和getter 方便实例使用 ================================== public Boolean getFlag() { return flag; } public void setFlag(Boolean flag) { this.flag = flag; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } public Boolean getTkexp() { return tkexp; } public void setTkexp(Boolean tkexp) { this.tkexp = tkexp; } public String getHurl() { return hurl; } public void setHurl(String hurl) { this.hurl = hurl; } public Boolean getExterr() { return exterr; } public void setExterr(Boolean exterr) { this.exterr = exterr; } public Date getTime() { return time; } public void setTime(Date time) { this.time = time; } }
开始测试自动推断能力:
{"flag":true,"code":0,"msg":"ok","data":null,"tkexp":false,"hurl":null,"exterr":false,"time":"2020-06-20 20:10:07"}
{ "flag": true, "code": 0, "msg": "ok", "data": { "userId": null, "userName": "小李飞刀", "userPwd": null, "userStatus": null, "userType": null, "userVersion": null, "registerTime": null, "lastLoginTime": null, "fuwuId": null }, "tkexp": false, "hurl": null, "exterr": false, "time": "2020-06-20 20:11:16" }
{ "flag": true, "code": 0, "msg": "ok", "data": [ { "userId": null, "userName": "小李飞刀", "userPwd": null, "userStatus": null, "userType": null, "userVersion": null, "registerTime": null, "lastLoginTime": null, "fuwuId": null } ], "tkexp": false, "hurl": null, "exterr": false, "time": "2020-06-20 20:12:58" }
{ "flag": true, "code": 0, "msg": "ok", "data": [ { "userId": null, "userName": "小李飞刀", "userPwd": null, "userStatus": null, "userType": null, "userVersion": null, "registerTime": null, "lastLoginTime": null, "fuwuId": null } ], "tkexp": false, "hurl": null, "exterr": false, "time": "2020-06-20 20:15:03" }
统统都没有任何问题,全程不报任何泛型警告,完美无瑕。 非常的方便使用;可见 Java 泛型 牛逼啊。
本文来自博客园,作者:del88,转载请注明原文链接:https://www.cnblogs.com/del88/p/13170216.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人