经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
SpringBoot定义优雅全局统一Restful API 响应框架六
来源:cnblogs  作者:程序员三时  时间:2023/6/14 11:23:57  对本文有异议

闲话不多说,继续优化 全局统一Restful API 响应框架 做到项目通用 接口可扩展。

如果没有看前面几篇文章请先看前面几篇

SpringBoot定义优雅全局统一Restful API 响应框架

SpringBoot定义优雅全局统一Restful API 响应框架二

SpringBoot定义优雅全局统一Restful API 响应框架三

SpringBoot定义优雅全局统一Restful API 响应框架四

SpringBoot定义优雅全局统一Restful API 响应框架五

这里讲一讲最后的版本和需要修复的一些问题

  1. @PostMapping("/add/UserApiCombo")
  2. public R addApiCombo(@RequestBody @Validated UserApplyApiComboDto userApplyApiComboDto) {
  3. userApiComboService.addApiCombo(userApplyApiComboDto);
  4. return R.success();
  5. }

我们看看这个代码,有什么问题。 我们返回了统一的封装结果集R 但是后面所有的controller 都这么写不太友好。

  1. 返回内容这么不够明确具体
  2. 所有controller 这么写增加重复工作量

我们可以这么去优化:

Spirng 提供了 ResponseBodyAdvice 接口,支持在消息转换器执行转换之前,对接口的返回结果进行处理,再结合 @ControllerAdvice 注解即可轻松支持上述功能

  1. package cn.soboys.springbootrestfulapi.common.handler;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.map.MapUtil;
  4. import cn.soboys.springbootrestfulapi.common.error.ErrorDetail;
  5. import cn.soboys.springbootrestfulapi.common.resp.R;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.springframework.core.MethodParameter;
  8. import org.springframework.http.MediaType;
  9. import org.springframework.http.converter.HttpMessageConverter;
  10. import org.springframework.http.server.ServerHttpRequest;
  11. import org.springframework.http.server.ServerHttpResponse;
  12. import org.springframework.web.bind.annotation.ControllerAdvice;
  13. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  14. /**
  15. * @author 公众号 程序员三时
  16. * @version 1.0
  17. * @date 2023/6/12 12:17 下午
  18. * @webSite https://github.com/coder-amiao
  19. * @Slf4j
  20. * @ControllerAdvice
  21. */
  22. @Slf4j
  23. @ControllerAdvice
  24. public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
  25. /**
  26. * supports方法: 判断是否要执行beforeBodyWrite方法,
  27. * true为执行,false不执行.
  28. * 通过该方法可以选择哪些类或那些方法的response要进行处理, 其他的不进行处理.
  29. *
  30. * @param returnType
  31. * @param converterType
  32. * @return
  33. */
  34. @Override
  35. public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
  36. return true;
  37. }
  38. /**
  39. * beforeBodyWrite方法: 对response方法进行具体操作处理
  40. * 实际返回结果业务包装处理
  41. *
  42. * @param body
  43. * @param returnType
  44. * @param selectedContentType
  45. * @param selectedConverterType
  46. * @param request
  47. * @param response
  48. * @return
  49. */
  50. @Override
  51. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
  52. if (body instanceof R) {
  53. return body;
  54. } else if (body == null) {
  55. return R.success();
  56. } else if (body instanceof ErrorDetail) {
  57. return body;
  58. } else if (body instanceof String) {
  59. return body;
  60. } else {
  61. return R.success().data(body);
  62. }
  63. }
  64. }

在实际controller 返回中我们直接返回数据内容就可以了

  1. @GetMapping("/home")
  2. public Student home() {
  3. Student s = new Student();
  4. s.setUserName("Tom");
  5. s.setAge(22);
  6. List hobby = new ArrayList();
  7. hobby.add("抽烟");
  8. hobby.add("喝酒");
  9. hobby.add("烫头");
  10. s.setHobby(hobby);
  11. s.setBalance(2229891.0892);
  12. s.setIdCard("420222199811207237");
  13. return s;
  14. }

我们目前版本中业务错误判断逻辑不是很友好,还需要优化,这里我们可以封装自己的业务异常
Assert(断言) 封装异常,让代码更优雅

符合 错误优先返回原则

正常我们业务异常代码是这样写的

  1. // 另一种写法
  2. Order order = orderDao.selectById(orderId);
  3. if (order == null) {
  4. throw new IllegalArgumentException("订单不存在。");
  5. }

使用断言优化后

  1. Order order = orderDao.selectById(orderId);
  2. Assert.notNull(order, "订单不存在。");

两种方式一对比,是不是明显感觉第一种更优雅,第二种写法则是相对丑陋的 if {...} 代码块。那么 神奇的 Assert.notNull() 背后到底做了什么呢?

这里就是我们需要优化代码

其实很多框架都带有Assert 工具包括JAVA JDK . SpringBoot,spring 也有自己的Assert
但是不符合我们自己的异常抛出业务逻辑,这里我们可以自定义自定的Assert 工具

我们来看一下部分源码

  1. public abstract class Assert {
  2. public Assert() {
  3. }
  4. public static void notNull(@Nullable Object object, String message) {
  5. if (object == null) {
  6. throw new IllegalArgumentException(message);
  7. }
  8. }
  9. }

可以看到,Assert 其实就是帮我们把 if {...} 封装了一下,是不是很神奇。虽然很简单,但不可否认的是编码体验至少提升了一个档次。

那么我们是不是可以模仿Assert也写一个自定义断言类,不过断言失败后抛出的异常不是IllegalArgumentException 这些内置异常,而是我们自己定义的异常。

  1. 定义公共异常
  1. package cn.soboys.springbootrestfulapi.common.exception;
  2. import cn.soboys.springbootrestfulapi.common.resp.ResultCode;
  3. import lombok.Data;
  4. /**
  5. * @author 公众号 程序员三时
  6. * @version 1.0
  7. * @date 2023/6/12 10:32 下午
  8. * @webSite https://github.com/coder-amiao
  9. */
  10. @Data
  11. public class BaseException extends RuntimeException {
  12. /**
  13. * 返回码
  14. */
  15. protected ResultCode resultCode;
  16. /**
  17. * 异常消息参数
  18. */
  19. protected Object[] args;
  20. public BaseException(ResultCode resultCode) {
  21. super(resultCode.getMessage());
  22. this.resultCode = resultCode;
  23. }
  24. public BaseException(String code, String msg) {
  25. super(msg);
  26. this.resultCode = new ResultCode() {
  27. @Override
  28. public String getCode() {
  29. return code;
  30. }
  31. @Override
  32. public String getMessage() {
  33. return msg;
  34. }
  35. @Override
  36. public boolean getSuccess() {
  37. return false;
  38. }
  39. ;
  40. };
  41. }
  42. public BaseException(ResultCode resultCode, Object[] args, String message) {
  43. super(message);
  44. this.resultCode = resultCode;
  45. this.args = args;
  46. }
  47. public BaseException(ResultCode resultCode, Object[] args, String message, Throwable cause) {
  48. super(message, cause);
  49. this.resultCode = resultCode;
  50. this.args = args;
  51. }
  52. }
  1. 所有其他异常继承公共异常
  1. package cn.soboys.springbootrestfulapi.common.exception;
  2. import cn.soboys.springbootrestfulapi.common.resp.ResultCode;
  3. /**
  4. * @author 公众号 程序员三时
  5. * @version 1.0
  6. * @date 2023/4/29 00:15
  7. * @webSite https://github.com/coder-amiao
  8. * 通用业务异常封装
  9. */
  10. public class BusinessException extends BaseException {
  11. public BusinessException(ResultCode resultCode, Object[] args, String message) {
  12. super(resultCode, args, message);
  13. }
  14. public BusinessException(ResultCode resultCode, Object[] args, String message, Throwable cause) {
  15. super(resultCode, args, message, cause);
  16. }
  17. }
  1. 断言业务异常类封装
  1. public interface Assert {
  2. /**
  3. * 创建异常
  4. * @param args
  5. * @return
  6. */
  7. BaseException newException(Object... args);
  8. /**
  9. * 创建异常
  10. * @param t
  11. * @param args
  12. * @return
  13. */
  14. BaseException newException(Throwable t, Object... args);
  15. /**
  16. * <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
  17. *
  18. * @param obj 待判断对象
  19. */
  20. default void assertNotNull(Object obj) {
  21. if (obj == null) {
  22. throw newException(obj);
  23. }
  24. }
  25. /**
  26. * <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
  27. * <p>异常信息<code>message</code>支持传递参数方式,避免在判断之前进行字符串拼接操作
  28. *
  29. * @param obj 待判断对象
  30. * @param args message占位符对应的参数列表
  31. */
  32. default void assertNotNull(Object obj, Object... args) {
  33. if (obj == null) {
  34. throw newException(args);
  35. }
  36. }
  37. }

具体使用

  1. /**
  2. * 异常返回模拟
  3. *
  4. * @return
  5. */
  6. @GetMapping("/exception")
  7. public Student exception() {
  8. Student s = null;
  9. BusinessErrorCode.Sign_Error.assertNotNull(s,"secret秘钥不正确");
  10. return s;
  11. }

在业务中我们可以通过这个方式直接抛出枚举异常。这样代码就简洁干净很多

代理已经更新到 github仓库脚手架项目

关注公众号,程序员三时 持续输出优质内容 希望给你带来一点启发和帮助

原文链接:https://www.cnblogs.com/kenx/p/17477454.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号