在前后端开发中,数据统一响应可以实现规范化的开发。接下来我们定义两个类,来实现数据的统一响应

自定义响应体

  1. @JsonInclude(JsonInclude.Include.NON_NULL)
  2. @Getter
  3. @Setter
  4. public class GeneralResponse<T> {
  5. @JsonProperty("status_code")
  6. private int statusCode;
  7. @JsonProperty("message")
  8. private String message;
  9. @JsonProperty("data")
  10. private T data;
  11. GeneralResponse(int statusCode) {
  12. this.statusCode = statusCode;
  13. }
  14. GeneralResponse(int statusCode, String message){
  15. this(statusCode);
  16. this.message = message;
  17. }
  18. GeneralResponse(int statusCode, T data){
  19. this(statusCode);
  20. this.data = data;
  21. }
  22. GeneralResponse(int statusCode, String message, T data){
  23. this(statusCode, message);
  24. this.data = data;
  25. }
  26. // success response
  27. public static <T> GeneralResponse<T> success(int statusCode){
  28. return new GeneralResponse<T>(statusCode);
  29. }
  30. public static <T> GeneralResponse<T> success(int statusCode,String message){
  31. return new GeneralResponse<T>(statusCode,message);
  32. }
  33. public static <T> GeneralResponse<T> success(int statusCode, T data){
  34. return new GeneralResponse<T>(statusCode,data);
  35. }
  36. public static <T> GeneralResponse<T> success(int statusCode,String message, T data){
  37. return new GeneralResponse<T>(statusCode,message,data);
  38. }
  39. // Fail response
  40. public static <T> GeneralResponse<T> fail(int statusCode){
  41. return new GeneralResponse<T>(statusCode);
  42. }
  43. public static <T> GeneralResponse<T> fail(int statusCode,String message){
  44. return new GeneralResponse<T>(statusCode,message);
  45. }
  46. public static <T> GeneralResponse<T> fail(int statusCode, T data){
  47. return new GeneralResponse<T>(statusCode,data);
  48. }
  49. public static <T> GeneralResponse<T> fail(int statusCode,String message, T data){
  50. return new GeneralResponse<T>(statusCode,message,data);
  51. }
  52. }

这里我们使用了注解 @JsonInclude(JsonInclude.Include.NON_NULL) 也就是说如果数据没有内容就会 显示为 null. @JsonProperty("xxx") 用于定义 JSON 内容的 KEY 值

枚举响应内容

为了使代码更具有可读性,我们可以定义一个枚举类来表示一些响应内容,如下

  1. package com.example.demo01.common;
  2. import jdk.nashorn.internal.objects.annotations.Getter;
  3. import jdk.nashorn.internal.objects.annotations.Setter;
  4. /**
  5. * @DATA: 2020/11/19
  6. */
  7. public enum CustomResult {
  8. SUCCESS(200, "Success!"),
  9. FAILED(300, "Failed"),
  10. ERROR(500, "ERROR");
  11. private int statusCode;
  12. private String message;
  13. CustomResult(int statusCode, String message) {
  14. this.statusCode = statusCode;
  15. this.message = message;
  16. }
  17. public int getStatusCode() {
  18. return statusCode;
  19. }
  20. public String getMessage() {
  21. return message;
  22. }
  23. public void setMessage(String message) {
  24. this.message = message;
  25. }
  26. public void setStatusCode(int statusCode) {
  27. this.statusCode = statusCode;
  28. }
  29. }

测试

我们在 controller 层测试一下效果

  1. @RestController
  2. @RequestMapping("/api/v1")
  3. public class HomeController {
  4. @GetMapping("/index")
  5. public GeneralResponse<String> Home(){
  6. return GeneralResponse.success(CustomResult.SUCCESS.getStatusCode(),"Hello Spring boot");
  7. }
  8. }

得到
image.png

全局包装

一般完成上面的操作后,就可以定义出很规范的数据统一的响应。不过全局包装是方便了,但是灵活性变差了,这里仅做记录,方便以后查询

因为全局包装我们不知道返回是正确还是失败,所以我们只能 hardcode

  1. @JsonInclude(JsonInclude.Include.NON_NULL)
  2. @Getter
  3. @Setter
  4. public class GeneralResponse<T> {
  5. ...
  6. public GeneralResponse(T data){
  7. this(CustomResult.SUCCESS.getStatusCode());
  8. this.data = data;
  9. }
  10. ...
  11. }

然后我们需要自定义一个配置类

  1. package com.example.demo01.configuration;
  2. import com.example.demo01.common.GeneralResponse;
  3. import com.fasterxml.jackson.databind.ObjectMapper;
  4. import org.springframework.core.MethodParameter;
  5. import org.springframework.http.MediaType;
  6. import org.springframework.http.converter.HttpMessageConverter;
  7. import org.springframework.http.server.ServerHttpRequest;
  8. import org.springframework.http.server.ServerHttpResponse;
  9. import org.springframework.web.bind.annotation.RestControllerAdvice;
  10. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  11. /**
  12. * @DATA: 2020/11/19
  13. */
  14. // 定义要执行全局包装的模块
  15. @RestControllerAdvice(basePackages = {"com.example.demo01"})
  16. public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
  17. // 如果返回的时候已经是 GeneralResponse 那就无需操作。
  18. @Override
  19. public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) {
  20. return !returnType.getParameterType().equals(GeneralResponse.class);
  21. }
  22. @Override
  23. public Object beforeBodyWrite(Object o, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
  24. if(returnType.getParameterType().equals(String.class)){
  25. ObjectMapper objectMapper = new ObjectMapper();
  26. try{
  27. return objectMapper.writeValueAsString(new GeneralResponse<>(o));
  28. }catch (Exception e){
  29. }
  30. }
  31. return new GeneralResponse(o);
  32. }
  33. }

测试一下

@RestController
@RequestMapping("/api/v1")
public class HomeController {
    @GetMapping("/index")
    public String Home(){
        return "Hello Spring boot";
    }
}

直接返回 String,但是 Response 已经变了
image.png