**高并发、微服务 、性能调优实战案例100讲，所有案例均源于个人工作实战，均配合代码落地**

加我微信：itsoku，所有案例均提供在线答疑。



# 第14节 接口通用返回值设计与实现



## 前言

目前大部分系统都是前后端分离，后端提供接口，返回json数据，前端拿到数据进行处理。

后端接口返回值，可以采用固定的格式，这样前后端配合起来会更顺畅一些，也会节约很多时间。

这节课代码在 lesson013 模块中。



## 后端接口返回值通用格式

代码如下，通常会有4个字段

```java
public class Result<T> {
    /**
     * 请求是否处理成功？
     */
    private boolean success;
    /**
     * 数据，泛型类型，后端需要返回给前端的业务数据可以放到这个里面
     */
    public T data;
    /**
     * 提示消息，如success为false的时给用户的提示信息
     */
    private String msg;
    /**
     * 错误编码，某些情况下，后端可以给前端提供详细的错误编码，前端可以根据不同的编码做一些不同的操作
     */
    private String code;
}
```

对应的接口示例代码如下，返回值为Result类型

```java
@RestController
public class TestController {

    @GetMapping("/hello")
    public Result<String> hello() {
        return ResultUtils.success("欢迎大家学习《高并发 & 微服务 & 性能调优实战案例 100 讲》");
    }
    
}
```

前端调用此接口，看到的结果如下

```java
{
  "success": true,
  "data": "欢迎大家学习《高并发 & 微服务 & 性能调优实战案例 100 讲》",
  "msg": null,
  "code": null
}
```



## 异常情况处理

后端的接口中，通常，都是有一些校验功能的，比如登录接口中，需要验证用户名或密码是否正确，如果不正确需要提示前端：用户名或密码不正确，给前端返回下面的数据

```json
{
  "success": false,
  "data": null,
  "msg": "1001",
  "code": "用户名或密码错误"
}
```

代码中我们可以怎么写呢？后端校验不通过的时候，可以抛出一个业务异常，然后在全局异常处理中去处理这个异常，返回通用格式的结果。



## 具体怎么做呢？

### 自定义一个业务异常类

```java
public class BusinessException extends RuntimeException {
    private String code;

    /**
     * @param code    错误编码
     * @param message 错误提示
     */
    public BusinessException(String code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(String code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}
```

### 接口中抛出业务异常

登录接口可以用下面这种写法了，用户名不对的时候，抛出一个业务异常BusinessException

```java
@GetMapping("/login")
public Result<String> login(String name) {
    if (!"路人".equals(name)) {
        throw new BusinessException("1001", "用户名错误");
    } else {
        return ResultUtils.success("登录成功");
    }
}
```

## 全局异常中对BusinessException异常进行统一处理

BusinessException这个异常可以使用springboot中的全局异常处理器去处理。

我们需要定义一个全局异常处理器，代码如下

- 类上使用 @RestControllerAdvice 注解标注
- 注意看handleBusinessException方法，这个方法上有个 @ExceptionHandler(BusinessException.class) 注解，这个注解的值是 BusinessException，表示接口中抛出这个异常的时候，会进入到 handleBusinessException 方法中去处理，这个方法最后返回的也是通用的结果Result类型

```java
@RestControllerAdvice
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理业务异常
     *
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    public Result handleBusinessException(BusinessException e, HttpServletRequest request) {
        logger.info("请求：{}，发生异常：{}", request.getRequestURL(), e.getMessage(), e);
        return ResultUtils.error(e.getCode(), e.getMessage());
    }

}
```

> SpringBoot全局异常处理，如果有不理解的，可以去看看我的这篇文章：http://itsoku.com/course/6/224

此时我们验证下上面这个登录接口

访问：http://localhost:8080/login?name=路人，用户名正确，返回结果如下

```json
{
  "success": true,
  "data": "登录成功",
  "msg": null,
  "code": null
}
```

再来看用户名错误的情况

访问：http://localhost:8080/login?name=张三，用户名不匹配，此时登录接口会抛出BusinessException异常，然后被全局异常中的`GlobalExceptionHandler#handleBusinessException`方法处理，前端看到的返回结果如下：

```json
{
  "success": false,
  "data": null,
  "msg": "用户名或密码错误",
  "code": "1001"
}
```



## 案例：SpringBoot自带的参数校验功能异常处理

SpringBoot为我们提供了参数自动校验功能，很好用，看下面案例。

用户注册接口

```java
com.itsoku.lesson013.controller.TestController#register
    
@PostMapping("/userRegister")
public Result<Void> userRegister(@Validated @RequestBody UserRegisterRequest req) {
    return ResultUtils.success();
}
```

当参数校验不通过的时候，会自动抛出一个org.springframework.validation.BindException异常

对应的全局异常处理方法

```java
@ExceptionHandler(BindException.class)
public Result handleBindException(BindException e, HttpServletRequest request) {
    logger.info("请求：{}，发生异常：{}", request.getRequestURL(), e.getMessage(), e);
    String message = e.getAllErrors().get(0).getDefaultMessage();
    return ResultUtils.error(message);
}
```



## 全局其他异常处理

当上面的异常处理方法都无法匹配接口中的异常的时候，将走下面这个方法去处理异常，这个是用来对异常处理进行兜底的。

```java
/**
 * 处理其他异常
 *
 * @param e
 * @param request
 * @return
 */
@ExceptionHandler(Exception.class)
public Result handleException(Exception e, HttpServletRequest request) {
    logger.info("请求：{}，发生异常：{}", request.getRequestURL(), e.getMessage(), e);
    //会返回code为500的一个异常
    return ResultUtils.error(ErrorCode.SERVER_ERROR,"系统异常，请稍后重试");
}
```



## 提供的几个工具类

- com.itsoku.lesson013.common.ResultUtils：提供了创建Result对象的一些静态方法
- com.itsoku.lesson013.common.BusinessExceptionUtils：提供了创建BusinessException的一些静态方法
- com.itsoku.lesson013.common.ErrorCode：将系统中所有的错误编码可以放到这个类中集中化管理



## 源码

源码同样是放在我的《高并发&微服务&性能调优实战案例100讲》的代码中，有兴趣的可以点击左下角的小黄车了解下，感谢大家的观看。



# 高并发 & 微服务 & 性能调优实战案例100讲

## 已更新 14 节课

<span style="font-weight:bold; color:red">目前整个课程59块钱，一杯咖啡的价格，还没下手的朋友，赶紧了，马上要涨价了</span>。

```java
1. 分片上传实战
2. 通用并发处理工具类实战
3. 实现一个好用接口性能压测工具类
4. 超卖问题的4种解决方案，也是防止并发修改数据出错的通用方案
5. Semaphore实现接口限流实战
6. 并行查询，优化接口响应速度实战
7. 接口性能优化之大事务优化
8. 通用的Excel动态导出功能实战
9. 手写线程池管理器，管理&监控所有线程池
10. 动态线程池
11. 使用SpringBoot实现动态Job实战
12. 并行查询，性能优化利器，可能有坑
13. 幂等的4种解决方案，吃透幂等性问题
14. 接口通用返回值设计与实现
```



## 课程部分大纲，连载中。。。。

以下课程均来源于个人多年的实战，均提供原理讲解 && 源码落地

<span style="font-weight:bold; color:red">目前整个课程59块钱，一杯咖啡的价格，还没下手的朋友，赶紧了，马上要涨价了</span>。

```java
1. 分片上传实战
2. 通用并发处理工具类实战
3. 实现一个好用接口性能压测工具类
4. 超卖问题的4种解决方案，也是防止并发修改数据出错的通用方案
5. Semaphore实现接口限流实战
6. 并行查询，优化接口响应速度实战
7. 接口性能优化之大事务优化
8. 通用的Excel动态导出功能实战
9. 手写线程池管理器，管理&监控所有线程池
10. 动态线程池
11. 使用SpringBoot实现动态Job实战
12. 通用的幂等性工具类实战
13. 接口返回值通用设计
14. 接口太多，各种dto、vo不计其数，如何命名？
15. 一个业务太复杂了，方法太多，如何传参？
16. 如何统计接口耗时？
17. AOP实战接口日志打印功能
18. AOP实现业务操作日志记录功能
19. AOP实现MyBatis分页功能
20. SpringBoot读写分离实战
21. MQ专题：事务消息实战（防止消息丢失）
22. MQ专题：消息幂等消费通用方案实战
23. MQ专题：延迟消息通用方案实战
24. MQ专题：顺序消息通用方案实战
25. MQ专题：消息积压问题
26. 分布式事务：使用事务消息实现事务最终一致性
27. 分布式事务：通用的TCC分布式事务生产级代码落地实战
28. 分布式锁案例实战
29. 微服务中如何传递上下文？实战
30. 微服务链路日志追踪实战（原理&代码落地）
31. SpringBoot实现租户数据隔离
32. MyBatis进阶：封装MyBatis，实现通用的无SQL版CRUD功能，架构师必备
33. MyBatis进阶：自己实现通用分表功能，架构师必备
34. MyBatis进阶：实现多租户隔离ORM框架
35. SpringBoot中实现自动监听PO的变化，自动生成表结构
36. 分布式专题：其他实战课程等
37. 性能调优：如何排查死锁？
38. 性能调优：如何排查内存溢出？
39. 性能调优：CPU被打满，如何排查？
40. 性能调优：生产代码没生效，如何定位？
41. 性能调优：接口太慢，如何定位？
42. 性能调优：如何查看生产上接口的入参和返回值？
43. 性能调优：远程debug
44. 生产上出现了各种故障，如何定位？
45. 其他等各种实战案例。。。
。。。
```

