- 自定义统一返回类 和 全局异常控制
- 自定义状态码枚举类
- 统一结果返回类ResultData
- 实现ResponseBodyAdvice接口对全局控制管理数据统一格式进行返回
- 构建全局异常返回类以及自定义客制化异常返回类
- 客制化异常处理类
这里先推荐一个大佬的 自定义统一返回类,绝对清晰明了
缥缈Jam:【老鸟玩系列】
SpringBoot 如何统一后端返回格式?老鸟们都是这样玩的!
这里小付对其细化整理。
众所周知:前后端分离项目中,如果后端猿们传的一些奇葩数据给前端的话,可能会被前端人员暴打,至于为什么?不用说咱们都知道——比如说你就传一个data回去,或者就是前端传入的数据是一个非法数据,咱们后端传回去时,确定其合理等等一些列问题都是可能导致后端猿们渐渐灭绝的原因之一…
自定义状态码枚举类步骤1:我们首先需要定义一个状态码枚举类
在我们的 blog-common 中用于存放有关于实体的类
故就有了 blog-web ---- com.alascanfu.utils.resultData.ReturnCode
public enum ReturnCode {
RC200(200,"操作成功"),
RC999(999,"操作失败"),
RC100(100,"服务器开启限流保护,请稍后再试!"),
RC201(201,"服务器开启降级保护,请稍后再试!"),
RC202(202,"热点参数限流,请稍后再试!"),
RC203(203,"系统规则不满足要求,请稍后再试!"),
RC204(204,"授权规则不通过,请稍后再试!"),
RC403(403,"无访问权限,请联系管理员授予权限"),
RC404(404,"没有找到您所要访问的页面,请重试"),
RC401(401,"匿名用户访问无权限资源时的异常"),
RC500(500,"系统异常,请稍后重试~"),
INVALID_TOKEN(2001,"访问令牌不合法"),
ACCESS_DENIED(2003,"没有权限访问该资源"),
CLIENT_AUTHENTICATION_FAILED(1001,"客户端认证失败"),
USERNAME_OR_PASSWORd(1002,"用户名或者密码错误"),
UNSUPPORTED_GRANT_TYPE(1003,"不支持的认证模式");
;
private final int code;
private final String message;
ReturnCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode(){
return code ;
}
public String getMessage (){
return message;
}
}
这里是根据大佬 缥缈Jam 中的状态码进行改写了一部分,也可以进行对其增加状态情况定义特定的枚举类。
该类中需要有 自定义好构造器与get/set方法 用于获取状态结果枚举返回。
统一结果返回类ResultData步骤2:我们需要构建统一的结果返回类ResultData
blog-web ---- com.alascanfu.utils.resultData.ResultData
@Data public class ResultData{ private int status ; private String message ; private T data ; private long timestamp ; public ResultData() { this.timestamp = System.currentTimeMillis(); } public static ResultData success(T data){ ResultData resultData = new ResultData<>(); resultData.setStatus(ReturnCode.RC200.getCode()); resultData.setMessage(ReturnCode.RC200.getMessage()); resultData.setData(data); return resultData; } public static ResultData fail(int code,String message){ ResultData resultData = new ResultData<>(); resultData.setStatus(code); resultData.setMessage(message); return resultData; } }
统一结果的返回类:当我们需要进行返回的时候就可以直接通过如下方式:
@GetMapping("/returnStr")
public ResultData hello(){
return ResultData.success("Hello,World!~");
}
然后咱们启动项目通过浏览器测试:
http://localhost:8080/returnStr
你就可以得出如下结果:
当然我们也可以返回一个对象的数据信息:
编写一个用于返回对象的Controller:
@GetMapping("/returnObj")
public ResultData getUser(){
return ResultData.success(new User(101,"Alascanfu"));
}
然后再启动项目通过浏览器进行测试:
http://localhost:8080/returnObj
结果与我们想要获取的数据是一致的:
但是你有没有发现,如果我们每次都对其编写相同的代码
ResultData.success(xxx) 实现ResponseBodyAdvice接口对全局控制管理数据统一格式进行返回
步骤3:通过实现ResponseBodyAdvice接口对全局控制管理数据统一格式进行返回
如果业务庞大的话代码冗余程度就会非常的高,所以我们是否通过某些特性将其封装统一返回,只需要写一次,而每个Controller就专心用于返回数据就可以了呢?
咱们可以通过一个类标注@Controller的增强注解@ControllerAdvice 的同时使得该类去实现ResponseBodyAdvice这个接口。
正如大佬所说知其然知其所以然。
窥探ResponseBodyAdvice这个接口源码:
public interface ResponseBodyAdvice{ boolean supports(MethodParameter returnType, Class extends HttpMessageConverter>> converterType); @Nullable T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class extends HttpMessageConverter>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response); }
该接口的第一个方法是supports:是否支持对控制层返回值数据进行封装等操作
第二个方法 beforeBodyWrite:在执行数据返回时之前的一个方法,可以对数据进行处理,用来对其进行统一处理或者加密等等…
@ControllerAdvice注解是干什么的?
- 全局异常处理
- 全局数据绑定
- 全局数据预处理
如果上面看不懂,你就可以简单理解该注解就是为了实现全局控制管理的注解。
因为其是属于对Controller层的对数据统一封装的控制拦截功能
故 blog-web ---- com.alascanfu.interceptor.ResponseAdvice.java
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Autowired
ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//如果数据返回的是字符串类型,我们需要通过objectMapper将其转换为JSON数据传回给前端进行控制
if (body instanceof String){
return objectMapper.writeValueAsString(ResultData.success(body));
}
//如果数据返回的是ResultData类型直接返回无需继续封装
if (body instanceof ResultData){
return body;
}
return ResultData.success(body);
}
}
这样一来简单的统一结果返回类,以及全局控制统一结果进行返回就完成啦~
此时来进行编写代码进行测试,是否达到我们的设想需求:
TestController.java
@GetMapping("/helloWorld")
public String helloWorld(){
return "Hello,World!~";
}
@GetMapping("/helloUser")
public User helloUser(){
return new User(101,"Alascanfu");
}
启动项目进入浏览器进行测试,是否符合我们所预想那样
1.直接进行返回数据对象:
2.直接返回字符串数据:
构建全局异常返回类以及自定义客制化异常返回类步骤4:构建全局异常返回类以及自定义客制化异常返回类
上述统一数据进行返回的格式已经测试成功了的嘛,但是这只是对正常数据测试的返回,那如果是异常错误信息什么的?怎么办?
这时候就要请出咱们的全局异常处理类来完成异常处理了
- 首先咱们来编写用于测试的两个异常Controller
@GetMapping("/exception1")
public void error1(){
throw new RuntimeException("自定义异常");
}
@GetMapping("/exception2")
public void error2(){
int i = 10/0;
}
这里需要先利用postman进行测试,因为前端页面会自动重定向到/error页面
- 创建一个全局异常处理类进行对其进行统一结果返回处理
blog-web ---- com.alascanfu.handler.exception.RestExceptionHandler
@Slf4j @RestControllerAdvice public class RestExceptionHandler{ @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)//状态码500 public ResultData exception(Exception e){ log.error("服务器内部异常 ex=>{}",e.getMessage(),e); return ResultData.fail(ReturnCode.RC500.getCode(),e.getMessage()); } }
@RestControllerAdvice 也是一个组合注解:@ControllerAdvice 和 @Response
@ExceptionHandler: 针对异常处理类,这里是Exception 那么当前就是配置的默认全局异常的处理类。
@ResponseStatus: 针对于HttpStatus这个枚举类中出现的状态码进行处理 这里是对 服务器内部出现异常就进行处理。
当然需要注意的是:全局异常处理需要返回的是一个也是全局统一返回格式的数据,这里返回的数据并非由Controller代为返回所以只能通过调用ResultData.fail来进行返回异常处理的情况…
随后启动项目,通过浏览器对异常处理进行测试:
http://localhost:8080/exception2
客制化异常处理类
当然我们也可以构建我们自己的异常处理类——来客制化
- 该类需要继承RuntimeException就如之前new RuntimeException 那样一致。
- blog-web ---- com.alascanfu.handler.exception.CustomExceptionHandler
@Data
public class CustomExceptionHandler extends RuntimeException{
private int status ;
private String message ;
private String data ;
private long timestamp;
public CustomExceptionHandler(int status ,String message){
this.status = status;
this.message = message;
}
}
随后需要将自定义的异常处理类交付给全局异常处理进行控制
blog-web ---- com.alascanfu.handler.exception.RestExceptionHandler中追加自定义全局异常处理
@ExceptionHandler(CustomExceptionHandler.class) public ResultDatacustomException(CustomExceptionHandler e){ log.error("自定义异常 ex=>{}",e.getMessage(),e); return ResultData.fail(e.getStatus(),e.getMessage()); }
这样一来咱们就可以去测试自定义异常类啦~
@GetMapping("/customException")
public void error3(){
throw new CustomExceptionHandler(1234,"某不知名异常");
}
okk~大功告成,这就是自定义统一返回类 和 全局异常控制~



