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

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

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

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

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

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

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

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

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

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

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

我们可以这么去优化:

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

package cn.soboys.springbootrestfulapi.common.handler;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.soboys.springbootrestfulapi.common.error.ErrorDetail;
import cn.soboys.springbootrestfulapi.common.resp.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; /**
* @author 公众号 程序员三时
* @version 1.0
* @date 2023/6/12 12:17 下午
* @webSite https://github.com/coder-amiao
* @Slf4j
* @ControllerAdvice
*/
@Slf4j
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
/**
* supports方法: 判断是否要执行beforeBodyWrite方法,
* true为执行,false不执行.
* 通过该方法可以选择哪些类或那些方法的response要进行处理, 其他的不进行处理.
*
* @param returnType
* @param converterType
* @return
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
} /**
* beforeBodyWrite方法: 对response方法进行具体操作处理
* 实际返回结果业务包装处理
*
* @param body
* @param returnType
* @param selectedContentType
* @param selectedConverterType
* @param request
* @param response
* @return
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof R) {
return body;
} else if (body == null) {
return R.success();
} else if (body instanceof ErrorDetail) {
return body;
} else if (body instanceof String) {
return body;
} else {
return R.success().data(body);
}
}
}

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

    @GetMapping("/home")
public Student home() { Student s = new Student();
s.setUserName("Tom");
s.setAge(22);
List hobby = new ArrayList();
hobby.add("抽烟");
hobby.add("喝酒");
hobby.add("烫头");
s.setHobby(hobby);
s.setBalance(2229891.0892);
s.setIdCard("420222199811207237");
return s;
}

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

Assert(断言) 封装异常,让代码更优雅

符合 错误优先返回原则

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

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

使用断言优化后

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

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

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

其实很多框架都带有Assert 工具包括JAVA JDK . SpringBoot,spring 也有自己的Assert

但是不符合我们自己的异常抛出业务逻辑,这里我们可以自定义自定的Assert 工具

我们来看一下部分源码

public abstract class Assert {
public Assert() {
} public static void notNull(@Nullable Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
}

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

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

  1. 定义公共异常
package cn.soboys.springbootrestfulapi.common.exception;

import cn.soboys.springbootrestfulapi.common.resp.ResultCode;
import lombok.Data; /**
* @author 公众号 程序员三时
* @version 1.0
* @date 2023/6/12 10:32 下午
* @webSite https://github.com/coder-amiao
*/
@Data
public class BaseException extends RuntimeException {
/**
* 返回码
*/
protected ResultCode resultCode;
/**
* 异常消息参数
*/
protected Object[] args; public BaseException(ResultCode resultCode) {
super(resultCode.getMessage());
this.resultCode = resultCode;
} public BaseException(String code, String msg) {
super(msg);
this.resultCode = new ResultCode() {
@Override
public String getCode() {
return code;
} @Override
public String getMessage() {
return msg;
} @Override
public boolean getSuccess() {
return false;
} ;
};
} public BaseException(ResultCode resultCode, Object[] args, String message) {
super(message);
this.resultCode = resultCode;
this.args = args;
} public BaseException(ResultCode resultCode, Object[] args, String message, Throwable cause) {
super(message, cause);
this.resultCode = resultCode;
this.args = args;
} }
  1. 所有其他异常继承公共异常
package cn.soboys.springbootrestfulapi.common.exception;

import cn.soboys.springbootrestfulapi.common.resp.ResultCode;

/**
* @author 公众号 程序员三时
* @version 1.0
* @date 2023/4/29 00:15
* @webSite https://github.com/coder-amiao
* 通用业务异常封装
*/
public class BusinessException extends BaseException { public BusinessException(ResultCode resultCode, Object[] args, String message) {
super(resultCode, args, message);
} public BusinessException(ResultCode resultCode, Object[] args, String message, Throwable cause) {
super(resultCode, args, message, cause);
} }
  1. 断言业务异常类封装
public interface Assert {
/**
* 创建异常
* @param args
* @return
*/
BaseException newException(Object... args); /**
* 创建异常
* @param t
* @param args
* @return
*/
BaseException newException(Throwable t, Object... args); /**
* <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
*
* @param obj 待判断对象
*/
default void assertNotNull(Object obj) {
if (obj == null) {
throw newException(obj);
}
} /**
* <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
* <p>异常信息<code>message</code>支持传递参数方式,避免在判断之前进行字符串拼接操作
*
* @param obj 待判断对象
* @param args message占位符对应的参数列表
*/
default void assertNotNull(Object obj, Object... args) {
if (obj == null) {
throw newException(args);
}
}
}

具体使用

/**
* 异常返回模拟
*
* @return
*/
@GetMapping("/exception")
public Student exception() {
Student s = null;
BusinessErrorCode.Sign_Error.assertNotNull(s,"secret秘钥不正确");
return s;
}

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

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

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

SpringBoot定义优雅全局统一Restful API 响应框架六的更多相关文章

  1. Spring Boot入门系列(二十一)如何优雅的设计 Restful API 接口版本号,实现 API 版本控制!

    前面介绍了Spring Boot 如何快速实现Restful api 接口,并以人员信息为例,设计了一套操作人员信息的接口.不清楚的可以看之前的文章:https://www.cnblogs.com/z ...

  2. Java Fluent Restful API自动化测试框架

    这是一个Restful API自动化测试框架,这是一个能让你写出高可读性测试代码的测试框架! 项目目标 话说目前行业内,Restful API自动化测试框架已经不是稀罕物了,各个语言都有自己的实现机制 ...

  3. 只需一步,在Spring Boot中统一Restful API返回值格式与统一处理异常

    ## 统一返回值 在前后端分离大行其道的今天,有一个统一的返回值格式不仅能使我们的接口看起来更漂亮,而且还可以使前端可以统一处理很多东西,避免很多问题的产生. 比较通用的返回值格式如下: ```jav ...

  4. Slim - 超轻量级PHP Restful API构建框架

    下载源码包: http://www.slimframework.com/ 基于Slim的Restful API Sample: <?php require '/darjuan/Slim/Slim ...

  5. springboot集成swagger2构建RESTful API文档

    在开发过程中,有时候我们需要不停的测试接口,自测,或者交由测试测试接口,我们需要构建一个文档,都是单独写,太麻烦了,现在使用springboot集成swagger2来构建RESTful API文档,可 ...

  6. 【从0到1,搭建Spring Boot+RESTful API+Shiro+Mybatis+SQLServer权限系统】03、创建RESTful API,并统一处理返回值

    本节应用Spring对RESTful的支持,使用了如@RestController等注解实现RESTful控制器. 如果对Spring中的RESTful不太明白,请查看相关书籍 1.创建一个数据对象, ...

  7. restful api 错误

    简介 随着移动开发和前端开发的崛起,越来越多的 Web 后端应用都倾向于实现 Restful API.Restful API 是一个简单易用的前后端分离方案,它只需要对客户端请求进行处理,然后返回结果 ...

  8. Restful API 中的错误处理

    简介 随着移动开发和前端开发的崛起,越来越多的 Web 后端应用都倾向于实现 Restful API. Restful API 是一个简单易用的前后端分离方案,它只需要对客户端请求进行处理,然后返回结 ...

  9. 探讨Morest在RESTful API测试的行业实践

    摘要:在本文中,我们将重点探讨使用自动化智能化Morest测试技术在RESTful API测试的行业实践. 本文分享自华为云社区<[智能化测试专题]华为云API智能测试工具--Morest测试框 ...

  10. MongoDB最简单的入门教程之五-通过Restful API访问MongoDB

    通过前面四篇的学习,我们已经在本地安装了一个MongoDB数据库,并且通过一个简单的Spring boot应用的单元测试,插入了几条记录到MongoDB中,并通过MongoDB Compass查看到了 ...

随机推荐

  1. jQ的工具类方法

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 在Kubernetes(k8s)中使用GPU

    介绍 Kubernetes 支持对节点上的 AMD 和 NVIDIA GPU (图形处理单元)进行管理,目前处于实验状态. 修改docker配置文件 root@hello:~# cat /etc/do ...

  3. 全网最详细中英文ChatGPT-GPT-4示例文档-人工智能助手从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/curl命令源代码,小白也能学)

    目录 Introduce 简介 setting 设置 Prompt 提示 Sample response 回复样本 API request 接口请求 python接口请求示例 node.js接口请求示 ...

  4. 万字长文,带你彻底搞懂 HTTPS(文末附实战)

    大家好,我是满天星,欢迎来到我的技术角落,本期我将带你一起来了解 HTTPS. 前言 其实网上写 HTTPS 的文章也不少了,但是不少文章都是从原理上泛泛而谈,只讲概念,没有讲原因,作为小白,看完还是 ...

  5. C++重载的奥义之运算符重载

    0.引言 重载,顾名思义从字面上理解就是重复装载,打一个不恰当的比方,你可以用一个篮子装蔬菜,也可以装水果或者其它,使用的是同一个篮子,但是可以用篮子重复装载的东西不一样. 正如在之前的文章<重 ...

  6. Gateway服务网关+过滤器

    为什么需要网关 Gateway网关是我们服务的守门神,所有微服务的统一入口. 网关的核心功能特性: 请求路由 权限控制 限流 架构图: 权限控制:网关作为微服务入口,需要校验用户是是否有请求资格,如果 ...

  7. CSS 基础拾遗(核心知识、常见需求)

    本篇文章围绕了 CSS 的核心知识点和项目中常见的需求来展开.虽然行文偏长,但较基础,适合初级中级前端阅读,阅读的时候请适当跳过已经掌握的部分. 这篇文章断断续续写了比较久,也参考了许多优秀的文章,但 ...

  8. java生成机器码

    java根据系统参数生成每个计算机的唯一标识. 1. 获取CPU序列号 /** * 获取CPU序列号 * @return * @throws IOException */ public static ...

  9. 【Redis】Setninel 哨兵机制

    一.Sentinel 哨兵工作原理 Redis在2.6+以后引入哨兵机制,在2.8版本后趋于稳定状态,在生产环境中建议使用2.8版本以上的sentinel服务.sentinel集群用于监控redis集 ...

  10. 使用NineData定制企业级数据库规范

    1. 为什么需要数据库规范? 在企业级应用中,数据库是非常重要的一部分,它们存储着公司的核心数据,包括客户信息.订单.产品信息等等.如果这些数据没有得到妥善的管理,那么就会导致数据不一致.数据丢失.数 ...