ServerResponse(服务器统一响应数据格式)
ServerResponse(服务器统一响应数据格式)
前言:
其实严格来说,ServerResponse应该归类到common包中。但是我实在太喜欢这玩意儿了。而且用得也非常频繁,所以忍不住推荐一下。
借此机会,申明一点,这个系列的类并不是都是我原创的,都是我从各个项目中看到的,感觉非常赞,一点点攒起来的。当然后面也有我自己写的一些工具。重要的是学习,从中学习到知识,就算脱离了这些工具,我们也可以自己写一个。
场景:
这个场景我真的觉得只要写过接口的,都需要这个。
其实,在刚刚接触代码的时候,看到大佬接口返回的JSON。JSON里面除了必要的data外,还有各种状态码,状态说明什么的,感觉很厉害。后来渐渐明白了,这个东西是必须的,你不写试试,看与你交互的大佬会不会把你拍成肉饼。
演进:
1.直接返回请求的数据:
后端:呀,前端发来的这个请求,数据库没有对应数据啊。返回一个null吧。
前端:大哥,你返回给我一个null,是不是接口有问题啊?
后端:那是你请求的数据在数据库中没有。
前端:哦。那我知道了。
后端:呀,前端发来的这个请求,参数不对啊(可能必要参数为空什么的)。我要返回null。
前端:大哥,你给我返回个null,是数据库没有对应数据嘛?但是这个条件应该有数据啊。
后端:不是的,你请求的参数有问题啊。
前端:大哥,那你倒是给我要给回馈啊。否则,我还以为是你接口没数据呢。
后端:好的吧。让我想想。
2.返回一个对象ResultVo(包含data与code,data为请求的数据,code为状态码):
后端:嘿,兄弟。我想到了一个好办法,我写了一个ResultVo,它是这样的……%¥&¥……。
前端:好的。我了解了。
后端:呀,前端发来的这个请求,没有足够的权限啊。我要返回data=null&code=10。然后在常量表中设置一下。
前端:我刚刚无意间发现,你的code又增加了10,什么意思?
后端:啊。忘了告诉你了。code=10表示权限不足。
前端:那我需要就这个情况,给用户提供专门的说明呀。
后端:这样效率太低了。而且以后可能会有更复杂多变的情况。我得想想办法。
3.返回一个对象ResultVo2(新增msg属性,充当响应的说明):
后端:嘿,兄弟。我将原来的ResultVo进行了升级,它是这样的&……%&%&……。
前端:这挺不错的,以后很多地方,我可以直接显示msg就行了。但是,现在有一个问题,现在的code太多了。我每次进行处理时都要遍历判断,而我常常只需要判断这个响应是否成功了。
后端:这样啊。我还得再改进一下。
4.ServerResponse:
后端:请教大佬后,我得到了非常棒的解决方案。并且,我根据自己的业务情况,进行细微的调整,这下就没什么问题了。
前端&后端:我们感受到了效率的显著提升,以及最为重要的代码规范(契约)。
作用:
ServerResponse就是用来统一服务器接口调用的响应
代码:
package tech.jarry.learning;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import java.io.Serializable;
/**
* @Author: jarry
*/
// 确保序列化JSON时,如果是null对象,其key也会消失。
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
// 生成无参构造,确保在RPC调用时,不会出现反序列失败
@NoArgsConstructor
public class ServerResponse<T> implements Serializable {
private int status;
private String msg;
private T data;
private ServerResponse(int status) {
this.status = status;
}
private ServerResponse(int status, String msg) {
this.status = status;
this.msg = msg;
}
// 这里存在一个问题,如果构造函数传入的参数列表为(int,String),那么是调用上面的(int,String),还是这里的(int,T),毕竟T作为泛型是可以表示String的
// 答案是调用上面的(int,String)(可以理解为上面的是专业的)。那么有时候data作为T类型传入的就是String啊,岂不是就出问题了。这里会在下方对应的public函数处理
private ServerResponse(int status, T data) {
this.status = status;
this.data = data;
}
private ServerResponse(int status, String msg, T data) {
this.status = status;
this.msg = msg;
this.data = data;
}
// 使之不在JSON序列化结果当中
@JsonIgnore
// 可以快速进行成功与否的条件判断
public boolean isSuccess() {
return this.status == ResponseCode.SUCCESS.getCode();
}
@JsonIgnore
// 可以快速进行成功与否的条件判断,判断false时,不用加!。囧
public boolean isFail() {
return this.status != ResponseCode.SUCCESS.getCode();
}
public int getStatus() {
return status;
}
public String getMsg() {
return msg;
}
public T getData() {
return data;
}
// 快速构建返回结果
// 成功时的调用
public static <T> ServerResponse<T> createBySuccess() {
return new ServerResponse<T>(ResponseCode.SUCCESS.getCode());
}
public static <T> ServerResponse<T> createBySuccessMessage(String msg) {
return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg);
}
public static <T> ServerResponse<T> createBySuccess(T data) {
return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), data);
}
public static <T> ServerResponse<T> createBySuccess(String msg, T data) {
return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg, data);
}
// 失败时的调用
public static <T> ServerResponse<T> createByError() {
return new ServerResponse<T>(ResponseCode.ERROR.getCode(), ResponseCode.ERROR.getDesc());
}
public static <T> ServerResponse<T> createByErrorMessage(String errorMessage) {
return new ServerResponse<T>(ResponseCode.ERROR.getCode(), errorMessage);
}
public static <T> ServerResponse<T> createByErrorCodeMessage(int errorCode, String errorMessage) {
return new ServerResponse<T>(errorCode, errorMessage);
}
}
依赖:
lombok(绝对的效率工具,值得推荐)
应用:
package tech.jarry.learning.terminal.client;
import com.renewable.terminal.terminal.common.ServerResponse;
import com.renewable.terminal.terminal.entity.Terminal;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* @Description:通过feign,对外提供termina服务的调用接口
* @Author: jarry
*/
@FeignClient(name = "terminal", fallback = TerminalClient.TerminalClientFallback.class)
public interface TerminalClient {
@PostMapping("/terminal/update_from_center.do")
ServerResponse updateTerminalFromCenter(@RequestBody Terminal terminal);
@PostMapping("/terminal/update.do")
ServerResponse updateTerminal(@RequestBody Terminal terminal);
@GetMapping("/terminal/refresh.do")
ServerResponse refreshTerminal();
@Component
public static class TerminalClientFallback implements TerminalClient {
@Override
public ServerResponse updateTerminalFromCenter(@RequestBody Terminal terminal){
return ServerResponse.createByErrorMessage("Busy service about Terminal/updateTerminalFromCenter().");
}
@Override
public ServerResponse updateTerminal(Terminal terminal) {
return ServerResponse.createByErrorMessage("Busy service about Terminal/updateTerminal().");
}
@Override
public ServerResponse refreshTerminal(){
return ServerResponse.createByErrorMessage("Busy service about Terminal/refreshTerminal().");
}
}
}
package tech.jarry.learning.terminal.controller;
import com.renewable.terminal.message.client.TerminalMessageClient;
import com.renewable.terminal.terminal.common.ServerResponse;
import com.renewable.terminal.terminal.entity.Terminal;
import com.renewable.terminal.terminal.service.ITerminalService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* <p>
* 前端控制器
* </p>
*
* @author jarry
* @since 2019-07-22
*/
@RestController
@RequestMapping("/terminal/")
public class TerminalController {
@Autowired
private ITerminalService iTerminalService;
@GetMapping("get_terminal.do")
@ResponseBody
public ServerResponse getTerminal(){
return iTerminalService.getTerminal();
}
@PostMapping("update.do")
@ResponseBody
public ServerResponse updateTerminal(@RequestBody Terminal terminal){
boolean result = iTerminalService.updateById(terminal);
iTerminalService.refresh();
if (!result){
return ServerResponse.createByErrorMessage("fail !");
}
return ServerResponse.createBySuccess(terminal);
}
@PostMapping("update_from_center.do")
@ResponseBody
public ServerResponse updateTerminalFromCenter(@RequestBody Terminal terminal){
boolean result = iTerminalService.updateById(terminal);
if (!result){
return ServerResponse.createByErrorMessage("fail !");
}
return ServerResponse.createBySuccessMessage("success");
}
@GetMapping("refresh.do")
@ResponseBody
public ServerResponse refreshTerminal(){
return iTerminalService.refresh();
}
}
问题:
在使用ServerResponse的过程中,曾经遇到一个问题。
那就是ServerResponse在SpringCloud架构中的Feign中的RPC调用中,无法进行反序列化。
找到的解释是,缺乏无参构造器(如果类中具有任意构造器,JVM就不会提供默认的无参构造器)。
所以在类的开头增加了@NoArgsConstructor,使得类具备无参构造器,问题解决。
总结:
作为服务器响应的统一数据格式,网上有很多的写法。这个ServerResponse也不一定是最好的。即使是最好的,也不一定是最适合你的。
往往我们在项目中需要一些工具实现一些特定功能,在实现功能之后,都或多或少会对现有的工具做一些调整,使得其更适合自己现有项目。
所以说,最好的不一定最适合。我们需要根据现有的情况,进行调整,重构,乃至自研。
题外话:
偷偷地推荐一下自己的个人博客。目前这个博客还处于测试阶段。还有很多的调整,之后还会与我自己微信公众号绑定,目测需要到今年下半年,才能全部完成。囧。有什么意见也可以提一提。
另外,由于还在测试阶段,所以如果哪天看不了,实属正常。囧
ServerResponse(服务器统一响应数据格式)的更多相关文章
- Spring Cloud实战 | 第九篇:Spring Cloud整合Spring Security OAuth2认证服务器统一认证自定义异常处理
本文完整代码下载点击 一. 前言 相信了解过我或者看过我之前的系列文章应该多少知道点我写这些文章包括创建 有来商城youlai-mall 这个项目的目的,想给那些真的想提升自己或者迷茫的人(包括自己- ...
- Timeout 时间已到。在操作完成之前超时时间已过或服务器未响应。
近来遇到这样一个错误:Timeout 时间已到.在操作完成之前超时时间已过或服务器未响应.错误截图如下: 错误原因分析:产生错误时我执行的操作需要的执行时间比较长.我测试了一下,那个操作用到的存储过程 ...
- 超时时间已到。在操作完成之前超时时间已过或服务器未响应。 (.Net SqlClient Data Provider)
超时时间已到.在操作完成之前超时时间已过或服务器未响应. (.Net SqlClient Data Provider) 在做一个小东西的时候出现了这个问题,就是使用VS调试几次项目后,使用SQL Se ...
- WebForm+Web.config: 超时时间已到。在操作完成之前超时时间已过或服务器未响应。
ylbtech-Error-WebForm+Web.config: 超时时间已到.在操作完成之前超时时间已过或服务器未响应. 超时时间已到.在操作完成之前超时时间已过或服务器未响应. 1.A,错误代码 ...
- timeout Timeout时间已到.在操作完成之前超时时间已过或服务器未响应
Timeout时间已到.在操作完成之前超时时间已过或服务器未响应 问题 在使用asp.net开发的应用程序查询数据的时候,遇到页面请求时间过长且返回"Timeout时间已到.在操作完成之间超 ...
- jQuery的$.ajax方法响应数据类型有哪几种?本质上原生ajax响应数据格式有哪几种,分别对应哪个属性?
jQuery的$.ajax方法响应数据类型有:xml.html.script.json.jsonp.text 本质上原生ajax响应数据格式只有2种:xml和text,分别对应xhr.response ...
- (摘)timeout Timeout时间已到.在操作完成之前超时时间已过或服务器未响应的几种情况
Timeout时间已到.在操作完成之前超时时间已过或服务器未响应 问题 在使用asp.net开发的应用程序查询数据的时候,遇到页面请求时间过长且返回"Timeout时间已到.在操作完成之间超 ...
- [C#.net]SqlDataAdapter 执行超时已过期 完成操作之前已超时或服务器未响应
随着数据库数据的不断增大,查询时间也随之增长.而客户端与数据库连接时间以及命令的执行时间都是有限的.默认为30s.所以在查询数据的时候,程序会出现 “超时时间已到.在操作完成之前超时时间已过或服务器未 ...
- WebAPI接口设计:SwaggerUI文档 / 统一响应格式 / 统一异常处理 / 统一权限验证
为什么还要写这类文章?因为我看过网上很多讲解的都不够全面,而本文结合实际工作讲解了swaggerui文档,统一响应格式,异常处理,权限验证等常用模块,并提供一套完善的案例源代码,在实际工作中可直接参考 ...
随机推荐
- Bzoj 1229: [USACO2008 Nov]toy 玩具 题解 三分+贪心
1229: [USACO2008 Nov]toy 玩具 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 338 Solved: 136[Submit] ...
- 理解vue-loader
事情的起源是被人问到,一个以.vue结尾的文件,是如何被编译然后运行在浏览器中的?突然发现,对这一块模糊的很,而且看mpvue的文档,甚至小程序之类的都是实现了自己的loader,所以十分必要抽时间去 ...
- Java监控任务的生命周期
Observable: public interface Observable { enum Cycle{ STARTED, RUNNING, DONE, ERROR } Cycle getCycle ...
- Java线程安全与数据同步
import java.util.HashMap; import java.util.concurrent.TimeUnit; public class Test { public static vo ...
- C# Winform --xml文件
背景: 在工作中,学习和使用OPC Server/Client系统时,发现开发的设计结构是把设备PLC的TAGLIST写为XML文件,在程序启动的时候载入从而完成自动配置. 从而开始了C# ASP.N ...
- NPM介绍
惠善一的博客:https://huishanyi.club NPM(Node Package Manger),Node包管理工具.在安装完Node之后,NPM便已经同时安装完成,用户可以通过NPM将自 ...
- KafKa 发消息到Storm
通过kafka客户端发送数据,由KafkaSpout进行接收消息,传输到ConsumerBolt进行实时数据处理. maven依赖 <dependencies> <!-- https ...
- JS系列1---节流,去抖(防抖)应用场景:intput请求优化,页面监听
在项目开发过程中经常遇到在input的change事件中发起请求,将用户最新输入的字符作为data传给后台,但是如果用户的输入频率过高,或者用户输入的字符还未拼成一个完整的字词,这时候发起请求会浪费网 ...
- Node.js实现简易的获取access_token
还是老样子,在自学node.js的道路上走得坑坑洼洼,按住了躁动的自己,调整好心情 ,ready........Go....! 首先在项目里新建config.json,其中 appid 与 appsc ...
- sqlserver2014创建数据库时,错误提示如下:尝试打开或创建物理‘c:\数据库\db.mdf’时,Create File遇到操作系统错误5(拒绝访问)
CREATE DATABASE test1 ON PRIMARY ( NAME =test1, FILENAME='C:\Program Files\test1.mdf',SIZE=10240K ...