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(服务器统一响应数据格式)的更多相关文章

  1. Spring Cloud实战 | 第九篇:Spring Cloud整合Spring Security OAuth2认证服务器统一认证自定义异常处理

    本文完整代码下载点击 一. 前言 相信了解过我或者看过我之前的系列文章应该多少知道点我写这些文章包括创建 有来商城youlai-mall 这个项目的目的,想给那些真的想提升自己或者迷茫的人(包括自己- ...

  2. Timeout 时间已到。在操作完成之前超时时间已过或服务器未响应。

    近来遇到这样一个错误:Timeout 时间已到.在操作完成之前超时时间已过或服务器未响应.错误截图如下: 错误原因分析:产生错误时我执行的操作需要的执行时间比较长.我测试了一下,那个操作用到的存储过程 ...

  3. 超时时间已到。在操作完成之前超时时间已过或服务器未响应。 (.Net SqlClient Data Provider)

    超时时间已到.在操作完成之前超时时间已过或服务器未响应. (.Net SqlClient Data Provider) 在做一个小东西的时候出现了这个问题,就是使用VS调试几次项目后,使用SQL Se ...

  4. WebForm+Web.config: 超时时间已到。在操作完成之前超时时间已过或服务器未响应。

    ylbtech-Error-WebForm+Web.config: 超时时间已到.在操作完成之前超时时间已过或服务器未响应. 超时时间已到.在操作完成之前超时时间已过或服务器未响应. 1.A,错误代码 ...

  5. timeout Timeout时间已到.在操作完成之前超时时间已过或服务器未响应

    Timeout时间已到.在操作完成之前超时时间已过或服务器未响应 问题 在使用asp.net开发的应用程序查询数据的时候,遇到页面请求时间过长且返回"Timeout时间已到.在操作完成之间超 ...

  6. jQuery的$.ajax方法响应数据类型有哪几种?本质上原生ajax响应数据格式有哪几种,分别对应哪个属性?

    jQuery的$.ajax方法响应数据类型有:xml.html.script.json.jsonp.text 本质上原生ajax响应数据格式只有2种:xml和text,分别对应xhr.response ...

  7. (摘)timeout Timeout时间已到.在操作完成之前超时时间已过或服务器未响应的几种情况

    Timeout时间已到.在操作完成之前超时时间已过或服务器未响应 问题 在使用asp.net开发的应用程序查询数据的时候,遇到页面请求时间过长且返回"Timeout时间已到.在操作完成之间超 ...

  8. [C#.net]SqlDataAdapter 执行超时已过期 完成操作之前已超时或服务器未响应

    随着数据库数据的不断增大,查询时间也随之增长.而客户端与数据库连接时间以及命令的执行时间都是有限的.默认为30s.所以在查询数据的时候,程序会出现 “超时时间已到.在操作完成之前超时时间已过或服务器未 ...

  9. WebAPI接口设计:SwaggerUI文档 / 统一响应格式 / 统一异常处理 / 统一权限验证

    为什么还要写这类文章?因为我看过网上很多讲解的都不够全面,而本文结合实际工作讲解了swaggerui文档,统一响应格式,异常处理,权限验证等常用模块,并提供一套完善的案例源代码,在实际工作中可直接参考 ...

随机推荐

  1. Connecting to the Network

    This lesson shows you how to implement a simple application that connects to the network. It explain ...

  2. activiti学习笔记

    activiti入门 activiti官网 pom.xml文件 xml <!-- activiti --> <dependency> <groupId>org.ac ...

  3. [Spring+SpringMVC+Mybatis]框架学习笔记(六):事务

    第7讲 事务 7.1 事务的概念 事务是一系列作为一个逻辑单元来执行的操作集合. 它是数据库维护数据一致性的单位,它讲数据库从一个一致状态,转变为新的另外一个一致状态.说的简单一点就是:如果一组处理步 ...

  4. Learning the Depths of Moving People by Watching Frozen

    基于双目的传统算法 对静止的物体, 在不同的 viewpoints 同一时刻进行拍摄, 根据拍摄到的结果, 使用三角测量算法计算出平面 2D 图像在 3D 图像中的坐标 单目 Ground Truth ...

  5. 掌握简单的Makefile文件编程

    Makefile描述整个程序的编译.链接规则 其中还包括了工程中用到的那些源文件及需要产生的目标文件 1)Makefile编程规则 目标(唯一):依赖(可多个) 命令... 伪目标 .PHONY:cl ...

  6. LaTeX大全

    1.指数和下标可以用^和_后加相应字符来实现.比如: 2.平方根(square root)的输入命令为:\sqrt,n 次方根相应地为: \sqrt[n].方根符号的大小由LATEX自动加以调整.也可 ...

  7. C语言入门5-键盘的输入和屏幕输出

    C程序中的键盘输入和屏幕输出都是通过  调用输入/输出函数  实现的. 一.数据的格式化    屏幕输出 函数printf()的一般格式  (有两种) (1)第一种: printf(格式控制字符串): ...

  8. [leetcode] 456. 132 Pattern (Medium)

    对一个三个元素以上的数组,如果存在1-3-2模式的组合,则返回true. 1-3-2模式就是值的排序是i<k<j但是下标排序是i<j<k. 解法一: 硬解,利用一个变量存储是否 ...

  9. python自动化测试之DDT数据驱动

    时隔已久,再次冒烟,自动化测试工作仍在继续,自动化测试中的数据驱动技术尤为重要,不然咋去实现数据分离呢,对吧,这里就简单介绍下与传统unittest自动化测试框架匹配的DDT数据驱动技术. 话不多说, ...

  10. 【css系列】六种实现元素水平居中方法

    一.前言 居中效果在CSS中很是普通的效果,平时大家所看到的居中效果主要分为三大类:水平居中.垂直居中和水平垂直居中.而其中水平居中相对于后两者来说要简单得多.使用了css3的flexbox的属性轻松 ...