JDK1.8,SpringBoot2.6.0

1、依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.6.0</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.example</groupId>
  12. <artifactId>demo</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>demo</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.projectlombok</groupId>
  26. <artifactId>lombok</artifactId>
  27. <optional>true</optional>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.springframework.boot</groupId>
  31. <artifactId>spring-boot-starter-test</artifactId>
  32. <scope>test</scope>
  33. </dependency>
  34. </dependencies>
  35. <build>
  36. <plugins>
  37. <plugin>
  38. <groupId>org.springframework.boot</groupId>
  39. <artifactId>spring-boot-maven-plugin</artifactId>
  40. <configuration>
  41. <excludes>
  42. <exclude>
  43. <groupId>org.projectlombok</groupId>
  44. <artifactId>lombok</artifactId>
  45. </exclude>
  46. </excludes>
  47. </configuration>
  48. </plugin>
  49. </plugins>
  50. </build>
  51. </project>

2、统一返回体类

  1. package com.example.demo.entity.vo;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Builder;
  4. import lombok.Data;
  5. import javax.servlet.http.HttpServletResponse;
  6. /**
  7. * @author: HanXu
  8. * on 2021/11/25
  9. * Class description: controller对外统一返回体
  10. */
  11. @Data
  12. @AllArgsConstructor
  13. @Builder
  14. public class BaseResponse<T> {
  15. private int code;
  16. private String msg;
  17. private T data;
  18. public BaseResponse(T data) {
  19. code = HttpServletResponse.SC_OK;
  20. msg = "success";
  21. this.data = data;
  22. }
  23. public BaseResponse(int code, String msg) {
  24. this.code = code;
  25. this.msg = msg;
  26. }
  27. }

工具类:

  1. package com.example.demo.utils;
  2. import com.fasterxml.jackson.annotation.JsonInclude;
  3. import com.fasterxml.jackson.core.JsonParser;
  4. import com.fasterxml.jackson.core.JsonProcessingException;
  5. import com.fasterxml.jackson.databind.DeserializationFeature;
  6. import com.fasterxml.jackson.databind.MapperFeature;
  7. import com.fasterxml.jackson.databind.ObjectMapper;
  8. import com.fasterxml.jackson.databind.SerializationFeature;
  9. import com.fasterxml.jackson.databind.module.SimpleModule;
  10. import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
  11. import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
  12. import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
  13. import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
  14. import lombok.AccessLevel;
  15. import lombok.NoArgsConstructor;
  16. import java.time.LocalDate;
  17. import java.time.LocalDateTime;
  18. import java.time.LocalTime;
  19. import java.time.format.DateTimeFormatter;
  20. import java.util.Objects;
  21. /**
  22. * @author: HanXu
  23. * on 2021/11/25
  24. * Class description:
  25. */
  26. @NoArgsConstructor(access = AccessLevel.PRIVATE)
  27. public abstract class JacksonUtil {
  28. private final static ObjectMapper objectMapper;
  29. static {
  30. objectMapper = initWrapperObjectMapper(new ObjectMapper());
  31. }
  32. /**
  33. * 转换Json
  34. *
  35. * @param object
  36. * @return
  37. */
  38. public static String toJson(Object object) {
  39. if (isCharSequence(object)) {
  40. return (String) object;
  41. }
  42. try {
  43. return getObjectMapper().writeValueAsString(object);
  44. } catch (JsonProcessingException e) {
  45. throw new RuntimeException(e);
  46. }
  47. }
  48. /**
  49. * 获取ObjectMapper
  50. *
  51. * @return
  52. */
  53. public static ObjectMapper getObjectMapper() {
  54. return objectMapper;
  55. }
  56. /**
  57. * 初始化 Wrapper ObjectMapper
  58. *
  59. * @param objectMapper
  60. * @return
  61. */
  62. public static ObjectMapper initWrapperObjectMapper(ObjectMapper objectMapper) {
  63. if (Objects.isNull(objectMapper)) {
  64. objectMapper = new ObjectMapper();
  65. }
  66. objectMapper.registerModule(new JavaTimeModule());
  67. objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
  68. //不显示为null的字段
  69. objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
  70. objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
  71. // 忽略不能转移的字符
  72. objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
  73. // 过滤对象的null属性.
  74. objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
  75. //忽略transient
  76. objectMapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
  77. SimpleModule simpleModule = new SimpleModule();
  78. simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  79. simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
  80. simpleModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
  81. // simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
  82. objectMapper.registerModule(simpleModule);
  83. return objectMapper;
  84. }
  85. /**
  86. * <p>
  87. * 是否为CharSequence类型
  88. * </p>
  89. *
  90. * @param object
  91. * @return
  92. */
  93. public static Boolean isCharSequence(Object object) {
  94. return !Objects.isNull(object) && isCharSequence(object.getClass());
  95. }
  96. /**
  97. * <p>
  98. * 是否为CharSequence类型
  99. * </p>
  100. *
  101. * @param clazz class
  102. * @return true 为是 CharSequence 类型
  103. */
  104. public static boolean isCharSequence(Class<?> clazz) {
  105. return clazz != null && CharSequence.class.isAssignableFrom(clazz);
  106. }
  107. /**
  108. * Json转换为对象 转换失败返回null
  109. *
  110. * @param json
  111. * @return
  112. */
  113. public static Object parse(String json) {
  114. Object object = null;
  115. try {
  116. object = getObjectMapper().readValue(json, Object.class);
  117. } catch (Exception ignored) {
  118. }
  119. return object;
  120. }
  121. /**
  122. * Json转换为对象 转换失败返回null
  123. *
  124. * @param json
  125. * @param clazz
  126. * @param <T>
  127. * @return
  128. */
  129. public static <T> T parseObject(String json, Class<T> clazz) {
  130. T t = null;
  131. try {
  132. t = getObjectMapper().readValue(json, clazz);
  133. } catch (Exception ignored) {
  134. }
  135. return t;
  136. }
  137. }

3、自定义注解

自定义注解@ResultUnite,该注解标注的类或方法,都能被包装成统一的返回。

wrapper包/annotation包下新建此类

  1. package com.example.demo.wrapper.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Inherited;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8. /**
  9. * @author: HanXu
  10. * on 2021/11/25
  11. * Class description: 统一返回结果注解
  12. */
  13. @Target({ ElementType.TYPE, ElementType.METHOD }) //作用范围
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @Documented
  16. @Inherited
  17. public @interface ResultUnite {
  18. }

然后使用@ControllerAdvice注解,来拦截所有 @RequestMapping 等注解标注的方法。这些方法返回时,不立刻返回,而是先经过 supports方法判断是否需要处理,返回true时,则执行beforeBodyWrite方法。

wrapper包/advice包下新建此类

  1. package com.example.demo.wrapper.advice;
  2. import com.example.demo.entity.vo.BaseResponse;
  3. import com.example.demo.utils.JacksonUtil;
  4. import com.example.demo.wrapper.annotation.ResultUnite;
  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. import java.lang.reflect.Method;
  13. /**
  14. * @author: HanXu
  15. * on 2021/11/25
  16. * Class description: 统一返回结果
  17. */
  18. @ControllerAdvice
  19. public class BaseResponseBodyAdvice implements ResponseBodyAdvice<Object> {
  20. private static final String CONVERT_NAME = "org.springframework.http.converter.StringHttpMessageConverter";
  21. private boolean isResultUnite(MethodParameter methodParameter, Class aClass) {
  22. Method method = methodParameter.getMethod();
  23. return aClass.isAnnotationPresent(ResultUnite.class) ||method.isAnnotationPresent(ResultUnite.class);
  24. }
  25. @Override
  26. public boolean supports(MethodParameter methodParameter, Class aClass) {
  27. return isResultUnite(methodParameter, aClass);
  28. }
  29. @Override
  30. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
  31. //处理String类型的返回值
  32. if (CONVERT_NAME.equalsIgnoreCase(selectedConverterType.getName())) {
  33. return JacksonUtil.toJson(new BaseResponse(body));
  34. }
  35. return new BaseResponse(body);
  36. }
  37. }

在supports方法中,我们的处理是:通过反射获取到执行方法/类上是否有@ResultUnite注解,有就返回true;无则返回false。

在beforeBodyWrite方法中,我们将方法的返回作为data传入BaseResponse构造方法中,重新new了一个BaseResponse对象,所以就达到了将返回数据包装成BaseResponse的目的。

以上两个类就给我们提供了一个具有能够包装返回数据的注解@ResultUnite

3.1、阶段测试

新建controller类测试一下

  1. package com.example.demo.controller;
  2. import com.example.demo.wrapper.annotation.ResultUnite;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. /**
  7. * @author: HanXu
  8. * on 2021/11/25
  9. * Class description:
  10. */
  11. @Slf4j
  12. @RestController
  13. public class TestController {
  14. @ResultUnite
  15. @RequestMapping("/test1")
  16. public String test1() {
  17. log.info("---------------test1---------------");
  18. return "111";
  19. }
  20. }

上述test1方法被本应该返回“111”的,但是被@ResultUnite注解了,所以执行观察,返回包装后的结果:

  1. {
  2. "code": 200,
  3. "msg": "success",
  4. "data": "111"
  5. }

再试一下:

  1. @ResultUnite
  2. @RequestMapping("/test2")
  3. public List<String> test2() {
  4. log.info("---------------test2---------------");
  5. List<String> list = new ArrayList<String>(){
  6. {
  7. add("123");
  8. add("456");
  9. }
  10. };
  11. return list;
  12. }

执行返回:

  1. {
  2. "code": 200,
  3. "msg": "success",
  4. "data": [
  5. "123",
  6. "456"
  7. ]
  8. }

这样我们就在任何成功的情况下,统一了对外部的返回(code,msg,data)。且实现非常清爽,在业务代码中只需要一个注解即可。

可是异常情况呢?异常情况因为不会再从controller中的return出去,所以就不受控制了。接下来处理异常情况。

4、统一异常处理

4.1、自定义异常类

这个类看起来和BaseResponse结构一样,那为什么不能使用BaseResponse代替呢?

因为此类需要继承RuntimeException,将来在代码中可以catch住此异常信息,然后做处理。况且两个类分别是不同功能的,即使属性一样,也不能复用。

  1. package com.example.demo.entity.vo;
  2. import com.example.demo.entity.enums.ExceptionCodeEnum;
  3. import lombok.Data;
  4. /**
  5. * @author: HanXu
  6. * on 2021/11/25
  7. * Class description: 自定义异常:方便进行异常信息扩展,且与RuntimeException区分开
  8. */
  9. @Data
  10. public class CustomerExceptionVO extends RuntimeException {
  11. private int code;
  12. private String msg;
  13. private Object data;
  14. public CustomerExceptionVO(ExceptionCodeEnum exceptionCodeEnum) {
  15. code = exceptionCodeEnum.getCode();
  16. msg = exceptionCodeEnum.getMsg();
  17. }
  18. public CustomerExceptionVO(int code, String msg) {
  19. super(msg);
  20. this.code = code;
  21. this.msg = msg;
  22. }
  23. public CustomerExceptionVO(int code, String msg, Object data) {
  24. super(msg);
  25. this.code = code;
  26. this.msg = msg;
  27. this.data = data;
  28. }
  29. }

枚举异常:

  1. package com.example.demo.entity.enums;
  2. import javax.servlet.http.HttpServletResponse;
  3. /**
  4. * @author: HanXu
  5. * on 2021/11/25
  6. * Class description: 通用异常枚举,供 CustomerException 使用
  7. */
  8. public enum ExceptionCodeEnum {
  9. //通用异常
  10. /**
  11. * 400
  12. */
  13. Bad_Request(HttpServletResponse.SC_BAD_REQUEST, "请求信息错误,请检查参数"),
  14. /**
  15. * 401
  16. */
  17. UnAuth(HttpServletResponse.SC_UNAUTHORIZED, "请先登陆"),
  18. /**
  19. * 403
  20. */
  21. Forbidden(HttpServletResponse.SC_FORBIDDEN, "无权查看"),
  22. /**
  23. * 404
  24. */
  25. Not_Found(HttpServletResponse.SC_NOT_FOUND, "未找到该路径或资源"),
  26. /**
  27. * 405
  28. */
  29. Method_Not_Allowed(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "请求方式不支持"),
  30. /**
  31. * 500
  32. */
  33. Internal_Server_Error(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务器异常"),
  34. /**
  35. * 503
  36. */
  37. Service_Unavailable(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "请求超时"),
  38. //自定义扩展其他业务异常
  39. /**
  40. * 400
  41. */
  42. Username_Or_PassWord_Error(HttpServletResponse.SC_BAD_REQUEST, "用户名密码错误"),
  43. /**
  44. * 400
  45. */
  46. Username_Exist(HttpServletResponse.SC_BAD_REQUEST, "用户名已存在"),
  47. /**
  48. * 400
  49. */
  50. ValidCode_Error(HttpServletResponse.SC_BAD_REQUEST, "验证码不正确"),
  51. ;
  52. private int code;
  53. private String msg;
  54. ExceptionCodeEnum(int code, String msg) {
  55. this.code = code;
  56. this.msg = msg;
  57. }
  58. public int getCode() {
  59. return code;
  60. }
  61. public String getMsg() {
  62. return msg;
  63. }
  64. public static ExceptionCodeEnum getByCode(int code) {
  65. ExceptionCodeEnum[] enums = ExceptionCodeEnum.values();
  66. for (ExceptionCodeEnum exceptionCodeEnum : enums) {
  67. if (exceptionCodeEnum.code == code) {
  68. return exceptionCodeEnum;
  69. }
  70. }
  71. return null;
  72. }
  73. }

工具类:

  1. package com.example.demo.utils;
  2. import java.text.SimpleDateFormat;
  3. import java.util.Date;
  4. /**
  5. * @author: HanXu
  6. * on 2021/11/25
  7. * Class description: 通用工具类
  8. */
  9. public class CommonUtil {
  10. public static Object getNotNull(Object original, Object backup) {
  11. if (backup == null) {
  12. throw new RuntimeException("备用参数不能为空");
  13. }
  14. return original != null ? original : backup;
  15. }
  16. public static Object getOrDefault(Object original, Object defaultValue) {
  17. return original != null ? original : defaultValue;
  18. }
  19. public static String getNonce() {
  20. return getNonce(8);
  21. }
  22. /**
  23. * 获取随机数 eg:0E35E147
  24. * @param n
  25. * @return
  26. */
  27. public static String getNonce(int n) {
  28. String nonce = "";
  29. //一个十六进制的值的数组
  30. String[] array = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
  31. //得到6个随机数
  32. for (int i = 0; i < n; i++) {
  33. int num = (int) (Math.random() * 16);
  34. nonce += array[num];
  35. }
  36. return nonce;
  37. }
  38. /**
  39. * 格式化获取当前时间
  40. * @return
  41. */
  42. public static String getNowDateFomart(){
  43. SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
  44. Date date = new Date();
  45. return format.format(date);
  46. }
  47. }

4.2、统一异常处理

使用@ControllerAdvice@ExceptionHandler注解,拦截代码中抛出的异常。拦截到属于CustomerExceptionVO的异常就优先handleCustomerException方法处理;否则就放行,看是否属于Exception异常(显然剩下的都是),然后由handleLeftException处理。

  1. package com.example.demo.exception;
  2. import com.example.demo.entity.vo.BaseResponse;
  3. import com.example.demo.entity.vo.CustomerExceptionVO;
  4. import com.example.demo.utils.CommonUtil;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.web.bind.annotation.ControllerAdvice;
  7. import org.springframework.web.bind.annotation.ExceptionHandler;
  8. import org.springframework.web.bind.annotation.ResponseBody;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletResponse;
  11. /**
  12. * @author: HanXu
  13. * on 2021/11/25
  14. * Class description: 统一异常处理
  15. */
  16. @Slf4j
  17. @ControllerAdvice
  18. @ResponseBody
  19. public class GlobalExceptionHandle {
  20. /**
  21. * 跟方法的上下顺序无关,系统优先给到捕获最小异常的方法,然后是大异常的方法
  22. * 只能捕获代码中手动抛出的 CustomerException 或 RuntimeException
  23. * @param e
  24. * @return
  25. */
  26. @ExceptionHandler(CustomerExceptionVO.class)
  27. public BaseResponse handleCustomerException(CustomerExceptionVO e, HttpServletRequest request) {
  28. log.error("异常request uri: {}", request.getRequestURI());
  29. log.error(e.getMessage(), e);
  30. return new BaseResponse((Integer) CommonUtil.getNotNull(e.getCode(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR), (String) CommonUtil.getOrDefault(e.getMsg(), e.getMessage()), e.getData());
  31. }
  32. /**
  33. * 捕获剩余所有异常
  34. * @param e
  35. * @param request
  36. * @return
  37. */
  38. @ExceptionHandler(Exception.class)
  39. public BaseResponse handleLeftException(Exception e, HttpServletRequest request) {
  40. log.error("异常request uri: {}", request.getRequestURI());
  41. log.error(e.getMessage(), e);
  42. return new BaseResponse(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务器异常");
  43. }
  44. }

4.3、意料之外错误的处理

当不是我们的代码抛出的异常时,GlobalExceptionHandle拦截不到,需要使用@RequestMapping(“error”)加ErrorController处理。

  1. package com.example.demo.exception;
  2. import com.example.demo.entity.enums.ExceptionCodeEnum;
  3. import com.example.demo.entity.vo.BaseResponse;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.boot.web.servlet.error.ErrorController;
  6. import org.springframework.stereotype.Controller;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.ResponseBody;
  9. import javax.servlet.http.HttpServletResponse;
  10. /**
  11. * @author: HanXu
  12. * on 2021/11/25
  13. * Class description: 拦截错误:GlobalExceptionHandle拦不到的异常(非代码中手动抛出的异常,上层SpringBoot中抛出的异常),当出错的时候,这里处理
  14. */
  15. @Slf4j
  16. @Controller
  17. @RequestMapping("error")
  18. public class GlobalErrorController implements ErrorController {
  19. @RequestMapping
  20. @ResponseBody
  21. public BaseResponse error(HttpServletResponse response) {
  22. int statusCode = response.getStatus();
  23. ExceptionCodeEnum exceptionCodeEnum = ExceptionCodeEnum.getByCode(statusCode);
  24. if (exceptionCodeEnum == null) {
  25. exceptionCodeEnum = ExceptionCodeEnum.Internal_Server_Error;
  26. }
  27. log.error("error: {}", exceptionCodeEnum.getMsg());
  28. BaseResponse baseResult = new BaseResponse(exceptionCodeEnum.getCode(), exceptionCodeEnum.getMsg());;
  29. return baseResult;
  30. }
  31. }

4.4、阶段测试

①、我们故意抛出一个异常:

  1. @ResultUnite
  2. @RequestMapping("/test3")
  3. public String test3() {
  4. log.info("---------------test3---------------");
  5. try {
  6. int i = 5 / 0;
  7. } catch (Exception e) {
  8. throw e;
  9. }
  10. return "111";
  11. }

执行测试:

  1. {
  2. "code": 500,
  3. "msg": "服务器异常",
  4. "data": null
  5. }

这个throw e;就是被GlobalExceptionHandle中的handleLeftException拦截到了。

②、让程序内部(非代码)抛出一个异常

  1. @ResultUnite
  2. @RequestMapping("/test4")
  3. public String test4() {
  4. log.info("---------------test4---------------");
  5. int i = 5 / 0;
  6. return "111";
  7. }

执行测试:

  1. {
  2. "code": 500,
  3. "msg": "服务器异常",
  4. "data": null
  5. }

也能被GlobalExceptionHandle中的handleLeftException拦截到。

③、某些情况需要抛出自定义异常

  1. @ResultUnite
  2. @RequestMapping("/test5")
  3. public String test5() {
  4. log.info("---------------test5---------------");
  5. //假装i是入参
  6. int i = 1;
  7. if (1 == i) {
  8. throw new CustomerExceptionVO(ExceptionCodeEnum.Forbidden);
  9. }
  10. return "111";
  11. }

执行测试:

  1. {
  2. "code": 403,
  3. "msg": "无权查看",
  4. "data": null
  5. }

被GlobalExceptionHandle中的handleCustomerException拦截到。

5、异常结束工具

在4.4的③中,这种情况应该是经常有的,我们需要对业务情况判断,然后众多不符合规则的case都异常返回了,这样每次都throw new CustomerExceptionVO是很麻烦的事情,所以可以搞一个工具来使用。

  1. package com.example.demo.exception;
  2. import com.example.demo.entity.enums.ExceptionCodeEnum;
  3. import com.example.demo.entity.vo.CustomerExceptionVO;
  4. import org.springframework.stereotype.Component;
  5. import javax.servlet.http.HttpServletResponse;
  6. /**
  7. * @author: HanXu
  8. * on 2021/11/25
  9. * Class description: 代码工具类:判断代码逻辑,不符合则抛出异常
  10. */
  11. @Component
  12. public class ApiAssert {
  13. public static void notNull(Object a) {
  14. if (a == null) {
  15. failure(ExceptionCodeEnum.Bad_Request);
  16. }
  17. if (a instanceof String) {
  18. if (((String) a).length() == 0) {
  19. failure(ExceptionCodeEnum.Bad_Request);
  20. }
  21. }
  22. }
  23. public static void notNull(Object a, String msg) {
  24. if (a == null) {
  25. badRequest(msg);
  26. }
  27. if (a instanceof String) {
  28. if (((String) a).length() == 0) {
  29. badRequest(msg);
  30. }
  31. }
  32. }
  33. //-------------------------400-----------------------
  34. public static void badRequest(String msg) {
  35. throw new CustomerExceptionVO(HttpServletResponse.SC_BAD_REQUEST, msg);
  36. }
  37. //--------------------------500-----------------------
  38. public static void failure(ExceptionCodeEnum exceptionCodeEnum) {
  39. throw new CustomerExceptionVO(exceptionCodeEnum);
  40. }
  41. public static void failure(String msg, Object data) {
  42. throw new CustomerExceptionVO(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg, data);
  43. }
  44. public static void failure(int code, String msg, Object data) {
  45. throw new CustomerExceptionVO(code, msg, data);
  46. }
  47. public static void failure(Object data) {
  48. throw new CustomerExceptionVO(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "error", data);
  49. }
  50. //--------------------------业务工具----------------------
  51. public static void forbidden() {
  52. throw new CustomerExceptionVO(ExceptionCodeEnum.Forbidden);
  53. }
  54. public static void internal() {
  55. throw new CustomerExceptionVO(ExceptionCodeEnum.Internal_Server_Error);
  56. }
  57. }

然后就可以在业务代码中使用:

  1. @ResultUnite
  2. @RequestMapping("/test6")
  3. public String test6() {
  4. log.info("---------------test6---------------");
  5. //假装i是入参
  6. int i = 1;
  7. if (1 == i) {
  8. ApiAssert.badRequest("请求不合法");
  9. }
  10. return "111";
  11. }

执行测试:

  1. {
  2. "code": 400,
  3. "msg": "请求不合法",
  4. "data": null
  5. }

这样就可以在任何需要的地方使用ApiAssert来结束并返回。

6、总结

上述统一返回体的处理只是一种处理方式,包含了正常结束,异常返回,业务代码自定义中断返回的处理。也许并不适合所有。但能包容大多数情况。

其实仍有许多改进的地方,比如抛出的异常错误码,完全可以自定义一个自己系统统一的错误码表,然后使用这个码表,就不用每次都使用HttpServletResponse的了。