ServerResponse (Server Unified Response Data Format)
Preface:
In fact, strictly speaking, ServerResponse should be categorized in the common package.But I really like it.It's also used so often that I can't help recommending it.
Take this opportunity to make it clear that not all the classes in this series were originally created by me, they were all the classes I saw from various projects, and I feel very impressed and saved little by little.Of course, there are some tools I wrote later.The important thing is to learn, learn from it, and even without these tools, we can write one by ourselves.
Scene:
This scene really feels like I need this whenever I have written an interface.
In fact, just when I touched the code, I saw the JSON returned by the big guy interface.In addition to the necessary data, JSON has a variety of status codes, which describe what it feels like.Later it became clear that this was a must. You don't write and try to see if the big guys who interacted with you would pat you into meat cakes.
Evolution:
1. Return the requested data directly:
Backend: Ah, this request from the front end, there is no corresponding data in the database.Return to a null.
Front End: Big Brother, did you return me a null? Is there something wrong with the interface?
Backend: That's the data you requested that is not in the database.
Front end: oh.I know that.
Backend: Ah, this request from the front end has incorrect parameters (what may be the necessary parameters are empty).I want to return to null.
Front End: Big Brother, give me a null. Is there no corresponding data in the database?But that should have data.
Backend: No, there is something wrong with the parameters you requested.
Front End: Big Brother, then you gave me feedback.Otherwise, I thought your interface had no data.
Backend: OK.Let me see.
2. Return an object, ResultVo (containing data and code, data is the requested data, code is the status code):
Backend: Hey, brother.I came up with a good idea. I wrote a ResultVo, and it was like this...%&....
Front end: OK.I understand.
Backend: Ah, the request from the front end does not have enough privileges.I want to return data=null&code=10.Then set it up in the constant table.
Front end: I just unintentionally found that your code has been added 10, what does it mean?
Backend: Ah.I forgot to tell you.code=10 indicates insufficient permissions.
Front-end: I need to give users specific instructions on this situation.
Backend: This is too inefficient.And there may be more complex and variable situations in the future.I have to find a way.
3. Return an object, ResultVo2 (new msg attribute, description of response):
Backend: Hey, brother.I upgraded my original ResultVo, and it was like this &...%&%&....
Front-end: That's good. In many places in the future, I can just display msg directly.However, there is a problem, there are too many code s.I go through the judgment every time I process it, and I often just need to decide if the response was successful.
Backend: That's it.I have to improve it a little more.
4.ServerResponse:
Backend: After consulting the big man, I got a great solution.And I make minor adjustments based on my business, which is fine.
Front-end&Back-end: We are experiencing significant improvements in efficiency and, most importantly, code specifications (contracts).
Effect:
ServerResponse is the response used to unify server interface calls
Code:
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 */ // Make sure that when you serialize the JSON, its key disappears if it is a null object. @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) // Generate parameterless constructs to ensure that deserialization failures do not occur during RPC calls @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; } // There is a problem here, if the list of parameters passed in by the constructor is (int,String), whether it calls above (int,String) or here (int,T), since T is generic enough to represent String // The answer is to call (int,String) above (which you can understand as professional).So sometimes when data comes in as a T type, it's a String, isn't that a problem?The corresponding public function below handles this 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; } // Make it out of the JSON serialization results @JsonIgnore // Conditional judgment of success or failure can be made quickly public boolean isSuccess() { return this.status == ResponseCode.SUCCESS.getCode(); } @JsonIgnore // Can quickly judge whether the condition of success or not, false, do not add!.Embarrassed public boolean isFail() { return this.status != ResponseCode.SUCCESS.getCode(); } public int getStatus() { return status; } public String getMsg() { return msg; } public T getData() { return data; } // Quick build returns // Call on success 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); } // Call on Failure 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); } }
Dependency:
lombok (absolute efficiency tool, recommended)
Application:
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: Call interface to provide termina services externally through feign * @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> * Front End Controller * </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(); } }
Question:
There was a problem in using ServerResponse.
That is, ServerResponse cannot be deserialized in an RPC call in Feign in the SpringCloud architecture.
The explanation found is the absence of parameterless constructors (JVM will not provide default parameterless constructors if there are any in the class).
So @NoArgsConstructor was added at the beginning of the class to give the class a parameterless constructor to solve the problem.
Summary:
As a uniform data format for server responses, there are many writings on the web.This ServerResponse is not necessarily the best.Even the best is not necessarily the best for you.
Often we need tools in our projects to implement specific functions, and after implementing the functions, we will more or less make adjustments to the existing tools to make them more suitable for our existing projects.
So the best is not necessarily the best.We need to adjust, refactor, and even do our own research based on the current situation.
Off-topic topic:
Secretly recommend your own Personal Blog .The blog is currently in beta.There are still many adjustments, which will be bound to my own WeChat public number after that. Visual monitoring will need to be completed in the second half of this year.Embarrassed.Any comments may also be mentioned.
In addition, because it is still in the testing stage, it is normal if you can't see it on any day.Embarrassed