在项目开发过程中,很难避免返回各种各样的结果,就会导致前端渲染时造成混乱,怎么统一返回呢?

编写R.java Object

  1. /**
  2. * @Author Joker DJ
  3. * @Date 2021/8/7 20:58
  4. * @Version 1.0
  5. */
  6. public class R {
  7. /**
  8. *标识返回状态
  9. */
  10. private Integer code;
  11. /**
  12. * 标识返回内容
  13. */
  14. private Object data;
  15. /**
  16. * 标识返回消息
  17. */
  18. private String message;
  19. /**
  20. * 成功返回
  21. * @param data
  22. * @return
  23. */
  24. public static R ok(Object data){
  25. return new R(RHttpStatusEnum.SUCCESS.getCode(),data,RHttpStatusEnum.SUCCESS.getMessage());
  26. }
  27. /**
  28. * 成功返回
  29. * @param data
  30. * @return
  31. */
  32. public static R ok(Object data,String message){
  33. return new R(RHttpStatusEnum.SUCCESS.getCode(),data,message);
  34. }
  35. /**
  36. * 失败返回
  37. * @param rHttpStatusEnum
  38. * @return
  39. */
  40. public static R error(RHttpStatusEnum rHttpStatusEnum){
  41. return new R(rHttpStatusEnum.getCode(),null,rHttpStatusEnum.getMessage());
  42. }
  43. public static R error(Integer code,String message){
  44. R r = new R();
  45. r.code(code);
  46. r.data(null);
  47. r.message(message);
  48. return r;
  49. }
  50. public R() {
  51. }
  52. public R(Integer code, Object data, String message) {
  53. this.code = code;
  54. this.data = data;
  55. this.message = message;
  56. }
  57. public Integer getCode() {
  58. return code;
  59. }
  60. public R code(Integer code) {
  61. this.code = code;
  62. return this;
  63. }
  64. public Object getData() {
  65. return data;
  66. }
  67. public R data(Object data) {
  68. this.data = data;
  69. return this;
  70. }
  71. public String getMessage() {
  72. return message;
  73. }
  74. public R message(String message) {
  75. this.message = message;
  76. return this;
  77. }
  78. }
  79. 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697

编写R.java 泛型

  1. package com.dj.rvo.Vo;
  2. /**
  3. * @Author Joker DJ
  4. * @Date 2021/8/7 20:58
  5. * @Version 1.0
  6. */
  7. public class RT<T> {
  8. /**
  9. * 标识返回状态
  10. */
  11. private Integer code;
  12. /**
  13. * 标识返回内容
  14. */
  15. private T data;
  16. /**
  17. * 标识返回消息
  18. */
  19. private String message;
  20. /**
  21. * 成功返回
  22. *
  23. * @param data
  24. * @return
  25. */
  26. public static <T> RT ok(T data) {
  27. return new RT(RHttpStatusEnum.SUCCESS.getCode(), data, RHttpStatusEnum.SUCCESS.getMessage());
  28. }
  29. /**
  30. * 成功返回
  31. *
  32. * @param data
  33. * @return
  34. */
  35. public static <T> RT ok(T data, String message) {
  36. return new RT(RHttpStatusEnum.SUCCESS.getCode(), data, message);
  37. }
  38. /**
  39. * 失败返回
  40. *
  41. * @param rHttpStatusEnum
  42. * @return
  43. */
  44. public static RT error(RHttpStatusEnum rHttpStatusEnum) {
  45. return new RT(rHttpStatusEnum.getCode(), null, rHttpStatusEnum.getMessage());
  46. }
  47. public static RT error(Integer code,String message){
  48. RT r = new RT();
  49. r.code(code);
  50. r.data(null);
  51. r.message(message);
  52. return r;
  53. }
  54. public RT() {
  55. }
  56. public RT(Integer code, T data, String message) {
  57. this.code = code;
  58. this.data = data;
  59. this.message = message;
  60. }
  61. public Integer getCode() {
  62. return code;
  63. }
  64. public RT code(Integer code) {
  65. this.code = code;
  66. return this;
  67. }
  68. public Object getData() {
  69. return data;
  70. }
  71. public RT data(T data) {
  72. this.data = data;
  73. return this;
  74. }
  75. public String getMessage() {
  76. return message;
  77. }
  78. public RT message(String message) {
  79. this.message = message;
  80. return this;
  81. }
  82. }
  83. 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899

编写返回枚举 RHttpStatusEnum

  1. package com.dj.rvo.Vo;
  2. /**
  3. * @Author Joker DJ
  4. * @Date 2021/8/7 21:16
  5. * @Version 1.0
  6. */
  7. public enum RHttpStatusEnum {
  8. SUCCESS(200,"success"),
  9. HTTP_NOT_FOUND(404,"未找到相关内容"),
  10. SERVER_ERROR( 500, "服务器忙,请稍后在试");
  11. private final int code;
  12. private final String message;
  13. RHttpStatusEnum(Integer code,String message){
  14. this.code=code;
  15. this.message=message;
  16. }
  17. public int getCode() {
  18. return code;
  19. }
  20. public String getMessage() {
  21. return message;
  22. }
  23. }
  24. 123456789101112131415161718192021222324252627282930

测试

  1. /**
  2. * 返回错误信息
  3. * @return
  4. */
  5. public R test1(){
  6. return R.error(RHttpStatusEnum.HTTP_NOT_FOUND);
  7. }
  8. /**
  9. * 返回成功信息
  10. * @return
  11. */
  12. public R test2() {
  13. return R.ok("成功");
  14. }
  15. public R test3() {
  16. return R.ok("数据","信息");
  17. }
  18. 123456789101112131415161718

问题:如果都返回R类 太强制了;通过通知来封装R类

编写 ResultResponseHandler

  1. package com.dj.rvo.Handler;
  2. import com.dj.rvo.Utils.JsonUtil;
  3. import com.dj.rvo.Vo.R;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. import org.springframework.core.MethodParameter;
  6. import org.springframework.http.MediaType;
  7. import org.springframework.http.converter.HttpMessageConverter;
  8. import org.springframework.http.server.ServerHttpRequest;
  9. import org.springframework.http.server.ServerHttpResponse;
  10. import org.springframework.web.bind.annotation.ControllerAdvice;
  11. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  12. /**
  13. * @Author Joker DJ
  14. * @Date 2021/8/7 21:53
  15. * @Version 1.0
  16. */
  17. @ControllerAdvice(basePackages = "com.dj.rvo.Controller")// 绑定的Controller包的权限定名 例如:com.dj.Controller
  18. public class ResultResponseHandler implements ResponseBodyAdvice<Object> {
  19. @Override
  20. public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
  21. return true;
  22. }
  23. @Override
  24. public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
  25. // 对请求的结果在这里统一返回和处理
  26. if (o instanceof ErrorHandler) {
  27. // 1、如果返回的结果是一个异常的结果,就把异常返回的结构数据倒腾到R.fail里面即可
  28. ErrorHandler errorHandler = (ErrorHandler) o;
  29. return R.error(errorHandler.getStatus(), errorHandler.getMessage());
  30. } else if (o instanceof String) {
  31. try {
  32. // 2、因为springmvc数据转换器对String是有特殊处理 StringHttpMessageConverter
  33. ObjectMapper objectMapper = new ObjectMapper();
  34. R r = R.ok(o);
  35. return objectMapper.writeValueAsString(r);
  36. }catch ( Exception ex){
  37. ex.printStackTrace();
  38. }
  39. }
  40. return R.ok(o);
  41. }
  42. }
  43. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647

编写自定义异常返回ErrorHandler

  1. package com.dj.rvo.Handler;
  2. import com.dj.rvo.Vo.RHttpStatusEnum;
  3. /**
  4. *
  5. * 功能描述: 异常返回
  6. *
  7. * @param:
  8. * @return:
  9. * @auther: Joker
  10. * @date: 2021/8/7 22:25
  11. */
  12. public class ErrorHandler {
  13. // ErrorHandler === R 答案:不想破坏R类。
  14. // 异常的状态码,从枚举中获得
  15. private Integer status;
  16. // 异常的消息,写用户看得懂的异常,从枚举中得到
  17. private String message;
  18. // 异常的名字
  19. private String exception;
  20. /**
  21. * 对异常处理进行统一封装
  22. *
  23. * @param resultCodeEnum 异常枚举
  24. * @param throwable 出现异常
  25. * @param message 异常的消息 null /by zero
  26. * @return
  27. */
  28. public static ErrorHandler fail(RHttpStatusEnum resultCodeEnum, Throwable throwable, String message) {
  29. ErrorHandler errorHandler = ErrorHandler.fail(resultCodeEnum, throwable);
  30. errorHandler.setMessage(message);
  31. return errorHandler;
  32. }
  33. /**
  34. * 对异常枚举进行封装
  35. *
  36. * @param resultCodeEnum
  37. * @param throwable
  38. * @return
  39. */
  40. public static ErrorHandler fail(RHttpStatusEnum resultCodeEnum, Throwable throwable) {
  41. ErrorHandler errorHandler = new ErrorHandler();
  42. errorHandler.setMessage(resultCodeEnum.getMessage());
  43. errorHandler.setStatus(resultCodeEnum.getCode());
  44. errorHandler.setException(throwable.getClass().getName());
  45. return errorHandler;
  46. }
  47. public Integer getStatus() {
  48. return status;
  49. }
  50. public void setStatus(Integer status) {
  51. this.status = status;
  52. }
  53. public String getMessage() {
  54. return message;
  55. }
  56. public void setMessage(String message) {
  57. this.message = message;
  58. }
  59. public String getException() {
  60. return exception;
  61. }
  62. public void setException(String exception) {
  63. this.exception = exception;
  64. }
  65. }
  66. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374

编写异常通知类 GlobalRestExceptionHandler

  1. package com.dj.rvo.Handler;
  2. import com.dj.rvo.Vo.RHttpStatusEnum;
  3. import com.fasterxml.jackson.core.JsonProcessingException;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. import org.springframework.validation.FieldError;
  6. import org.springframework.web.bind.MethodArgumentNotValidException;
  7. import org.springframework.web.bind.annotation.ExceptionHandler;
  8. import org.springframework.web.bind.annotation.RestControllerAdvice;
  9. import javax.servlet.http.HttpServletRequest;
  10. import java.util.ArrayList;
  11. import java.util.HashMap;
  12. import java.util.List;
  13. import java.util.Map;
  14. /**
  15. *
  16. * 功能描述: 统一异常处理
  17. *
  18. * @param:
  19. * @return:
  20. * @auther: Joker
  21. * @date: 2021/8/7 22:30
  22. */
  23. @RestControllerAdvice
  24. public class GlobalRestExceptionHandler {
  25. /**
  26. * 对服务器端出现500异常进行统一处理
  27. * 缺点:不明确
  28. * 场景:
  29. */
  30. @ExceptionHandler(Throwable.class)
  31. public ErrorHandler makeExcepton(Throwable e, HttpServletRequest request) {
  32. ErrorHandler errorHandler = ErrorHandler.fail(RHttpStatusEnum.SERVER_ERROR, e);
  33. return errorHandler;
  34. }
  35. /**
  36. * 对服务器端出现500异常进行统一处理
  37. * 缺点:明确异常信息
  38. */
  39. @ExceptionHandler(BusinessException.class)
  40. public ErrorHandler makeOrderException(BusinessException businessException, HttpServletRequest request) {
  41. ErrorHandler errorHandler = new ErrorHandler();
  42. errorHandler.setMessage(businessException.getMessage());
  43. errorHandler.setStatus(businessException.getCode());
  44. return errorHandler;
  45. }
  46. /**
  47. * 对验证的统一异常进行统一处理
  48. */
  49. @ExceptionHandler(MethodArgumentNotValidException.class)
  50. public ErrorHandler handlerValiator(MethodArgumentNotValidException e, HttpServletRequest request) throws JsonProcessingException {
  51. // 1: 从MethodArgumentNotValidException提取验证失败的所有的信息。返回一个List<FieldError>
  52. List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
  53. // 2: 把fieldErrors中,需要的部分提出出来进行返回
  54. List<Map<String, String>> mapList = toValidatorMsg(fieldErrors);
  55. // 3: 把需要的异常信息转换成json进行返回
  56. ObjectMapper objectMapper = new ObjectMapper();
  57. String mapJson = objectMapper.writeValueAsString(mapList);
  58. ErrorHandler errorHandler = ErrorHandler.fail(RHttpStatusEnum.SERVER_ERROR, e, mapJson);
  59. return errorHandler;
  60. }
  61. /**
  62. * 对验证异常进行统一处理提取需要的部分
  63. *
  64. * @param fieldErrorList
  65. * @return
  66. */
  67. private List<Map<String, String>> toValidatorMsg(List<FieldError> fieldErrorList) {
  68. List<Map<String, String>> mapList = new ArrayList<>();
  69. // 循环提取
  70. for (FieldError fieldError : fieldErrorList) {
  71. Map<String, String> map = new HashMap<>();
  72. // 获取验证失败的属性
  73. map.put("field", fieldError.getField());
  74. // 获取验证失败的的提示信息
  75. map.put("msg", fieldError.getDefaultMessage());
  76. mapList.add(map);
  77. }
  78. return mapList;
  79. }
  80. }
  81. 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091

自定义异常 BusinessException

  1. package com.dj.rvo.Handler;
  2. import com.dj.rvo.Vo.RHttpStatusEnum;
  3. /**
  4. *
  5. * 功能描述:
  6. *
  7. * @param:
  8. * @return:
  9. * @auther: Joker
  10. * @date: 2021/8/7 22:47
  11. */
  12. public class BusinessException extends RuntimeException {
  13. private Integer code;
  14. private String message;
  15. public BusinessException(RHttpStatusEnum resultCodeEnum) {
  16. this.code = resultCodeEnum.getCode();
  17. this.message = resultCodeEnum.getMessage();
  18. }
  19. public BusinessException(Integer code, String message) {
  20. this.code = code;
  21. this.message = message;
  22. }
  23. public Integer getCode() {
  24. return code;
  25. }
  26. public void setCode(Integer code) {
  27. this.code = code;
  28. }
  29. public String getMessage() {
  30. return message;
  31. }
  32. public void setMessage(String message) {
  33. this.message = message;
  34. }
  35. }
  36. 1234567891011121314151617181920212223242526272829303132333435363738394041

测试

  1. /**
  2. * @Author Joker DJ
  3. * @Date 2021/8/7 22:45
  4. * @Version 1.0
  5. */
  6. @RestController
  7. public class Test {
  8. @RequestMapping("/test1")
  9. public Map<String,Object> test1(){
  10. Map<String, Object> hashMap = new HashMap<>();
  11. hashMap.put("username","JokerDJ");
  12. return hashMap;
  13. }
  14. @RequestMapping("/test2")
  15. public String test2(){
  16. boolean flag = false;
  17. System.out.println(1/0);
  18. return "成功";
  19. }
  20. @RequestMapping("/test3")
  21. public String test3(){
  22. boolean flag = false;
  23. if(!flag){
  24. throw new BusinessException(500,"系统异常");
  25. }
  26. return "成功";
  27. }
  28. }
  29. 1234567891011121314151617181920212223242526272829303132

相关工具

  1. package com.dj.rvo.Utils;
  2. import org.codehaus.jackson.map.DeserializationConfig;
  3. import org.codehaus.jackson.map.ObjectMapper;
  4. import org.codehaus.jackson.map.SerializationConfig;
  5. import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
  6. import org.codehaus.jackson.type.JavaType;
  7. import org.codehaus.jackson.type.TypeReference;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. import org.springframework.util.StringUtils;
  11. import java.text.SimpleDateFormat;
  12. /**
  13. * by mofeng
  14. * <dependency>
  15. * <groupId>com.fasterxml.jackson.dataformat</groupId>
  16. * <artifactId>jackson-dataformat-avro</artifactId>
  17. * </dependency>
  18. *
  19. * <dependency>
  20. * <groupId>org.apache.commons</groupId>
  21. * <artifactId>commons-lang3</artifactId>
  22. * <version>3.6</version>
  23. * </dependency>
  24. */
  25. public class JsonUtil {
  26. private static ObjectMapper objectMapper = new ObjectMapper();
  27. private static Logger log = LoggerFactory.getLogger(JsonUtil.class);
  28. static {
  29. // 对象的所有字段全部列入
  30. objectMapper.setSerializationInclusion(Inclusion.ALWAYS);
  31. // 取消默认转换timestamps形式
  32. objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
  33. // 忽略空Bean转json的错误
  34. objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
  35. // 所有的日期格式都统一为以下的样式,即yyyy-MM-dd HH:mm:ss
  36. objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
  37. // 忽略 在json字符串中存在,但是在java对象中不存在对应属性的情况。防止错误
  38. objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  39. // 精度的转换问题
  40. objectMapper.configure(DeserializationConfig.Feature.USE_BIG_DECIMAL_FOR_FLOATS, true);
  41. objectMapper.configure(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
  42. }
  43. public static <T> String obj2String(T obj) {
  44. if (obj == null) {
  45. return null;
  46. }
  47. try {
  48. return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
  49. } catch (Exception e) {
  50. log.warn("Parse Object to String error", e);
  51. return null;
  52. }
  53. }
  54. public static <T> String obj2StringPretty(T obj) {
  55. if (obj == null) {
  56. return null;
  57. }
  58. try {
  59. return obj instanceof String ? (String) obj
  60. : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
  61. } catch (Exception e) {
  62. log.warn("Parse Object to String error", e);
  63. return null;
  64. }
  65. }
  66. public static <T> T string2Obj(String str, Class<T> clazz) {
  67. if (StringUtils.isEmpty(str) || clazz == null) {
  68. return null;
  69. }
  70. try {
  71. return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
  72. } catch (Exception e) {
  73. log.warn("Parse String to Object error", e);
  74. return null;
  75. }
  76. }
  77. public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
  78. if (StringUtils.isEmpty(str) || typeReference == null) {
  79. return null;
  80. }
  81. try {
  82. return (T) (typeReference.getType().equals(String.class) ? str
  83. : objectMapper.readValue(str, typeReference));
  84. } catch (Exception e) {
  85. log.warn("Parse String to Object error", e);
  86. return null;
  87. }
  88. }
  89. public static <T> T string2Obj(String str, Class<?> collectionClass, Class<?>... elementClasses) {
  90. JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
  91. try {
  92. return objectMapper.readValue(str, javaType);
  93. } catch (Exception e) {
  94. log.warn("Parse String to Object error", e);
  95. return null;
  96. }
  97. }
  98. public static void main(String[] args) {
  99. //String json = "{\"name\":\"Geely\",\"color\":\"blue\",\"id\":1274670369972846594}";
  100. /*
  101. * User user = new User(); user.setId(2);
  102. * user.setAccount("geely@happymmall.com"); user.setCreateTime(new Date());
  103. * String userJsonPretty = JsonUtil.obj2StringPretty(user);
  104. * log.info("userJson:{}",userJsonPretty);
  105. *
  106. *
  107. * User user2 = JsonUtil.string2Obj(userJsonPretty, User.class);
  108. * System.out.println(user2);
  109. */
  110. }
  111. }

分界

为什么使用

项目中我们会将响应封装成JSON返回,一般我们会将所有接口的数据格式统一, 使前端对数据的操作更一致、轻松。 一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数 据就可以。但是一般会包含状态码、返回消息、数据这几部分内容

一. 统一返回数据格式

  1. {
  2. "success": 布尔, //响应是否成功
  3. "code": 数字, //响应码
  4. "message": 字符串, //返回消息
  5. "data": HashMap //返回数据,放在键值对中
  6. }
  7. 123456

二 . 创建统一结果返回类

  • 抽取出来作为公共部分

1. 创建公共部分模块Common

2. 创建接口定义返回码 也可使用枚举 也可以不定义,直接写入

  1. public interface ResultCode {
  2. public static Integer SUCCESS = 20000;
  3. public static Integer ERROR = 20001;
  4. }
  5. 1234

3. 创建结果类

  1. //统一返回结果的类
  2. @Data
  3. public class R {
  4. @ApiModelProperty(value = "是否成功")
  5. private Boolean success;
  6. @ApiModelProperty(value = "返回码")
  7. private Integer code;
  8. @ApiModelProperty(value = "返回消息")
  9. private String message;
  10. @ApiModelProperty(value = "返回数据")
  11. private Map<String, Object> data = new HashMap<String, Object>();
  12. //把构造方法私有 不让别人new该类
  13. private R() {}
  14. //实现链式编程
  15. //R.ok().code().message().data();
  16. //成功静态方法
  17. public static R ok() {
  18. R r = new R();
  19. r.setSuccess(true);
  20. r.setCode(ResultCode.SUCCESS);
  21. r.setMessage("成功");
  22. return r;
  23. }
  24. //失败静态方法
  25. public static R error() {
  26. R r = new R();
  27. r.setSuccess(false);
  28. r.setCode(ResultCode.ERROR);
  29. r.setMessage("失败");
  30. return r;
  31. }
  32. public R success(Boolean success){
  33. this.setSuccess(success);
  34. return this; //返回this 实现链式编程
  35. }
  36. //实现链式编程
  37. //R.ok().code().message().data();
  38. public R message(String message){
  39. this.setMessage(message);
  40. return this;
  41. }
  42. public R code(Integer code){
  43. this.setCode(code);
  44. return this;
  45. }
  46. public R data(String key, Object value){
  47. this.data.put(key, value);
  48. return this;
  49. }
  50. public R data(Map<String, Object> map){
  51. this.setData(map);
  52. return this;
  53. }
  54. }
  55. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768

4. 项目结构

image-20210712223051406

三. 使用

1.在使用的包中先导依赖

2.接口方法返回结果都改为R(导包时记得引对)

  1. // 1 查询所有数据
  2. @GetMapping("findAll")
  3. public R findAllTeacher(){
  4. //调用service的方法实现查询所有的操作
  5. List<EduTeacher> list = teacherService.list(null);
  6. return R.ok().data("items",list); //链式编程
  7. }
  8. // 2. 逻辑删除方法
  9. @DeleteMapping("{id}")
  10. public R removeTeacher(@PathVariable String id){
  11. boolean flag = teacherService.removeById(id);
  12. if(flag){
  13. return R.ok();
  14. }else
  15. return R.error();
  16. }