• 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也不一定是最好的。即使是最好的,也不一定是最适合你的。

    往往我们在项目中需要一些工具实现一些特定功能,在实现功能之后,都或多或少会对现有的工具做一些调整,使得其更适合自己现有项目。

    所以说,最好的不一定最适合。我们需要根据现有的情况,进行调整,重构,乃至自研。

    题外话:

    偷偷地推荐一下自己的个人博客。目前这个博客还处于测试阶段。还有很多的调整,之后还会与我自己微信公众号绑定,目测需要到今年下半年,才能全部完成。囧。有什么意见也可以提一提。

    另外,由于还在测试阶段,所以如果哪天看不了,实属正常。囧

  • 相关阅读:
    到底什么级别才算是高并发?
    阿里大佬教你,如何写好 Java 代码!
    Java 13 发布了!
    年轻人的第一个自定义 Spring Boot Starter!
    懵圈了,面试官问一个 TCP 连接可发多少个 HTTP 请求?
    Java 和操作系统交互,你猜会发生什么?
    不用找了,基于 Redis 的分布式锁实战来了!
    中国剩余定理
    欧几里德与扩展欧几里德
    大数mod的技巧
  • 原文地址:https://www.cnblogs.com/Tiancheng-Duan/p/11363437.html
Copyright © 2020-2023  润新知