统一数据返回格式 及 可能遇到的问题;统一异常处理

简介: 统一数据返回格式需要创建一个新类并使其实现ResponseBodyAdvice 接口并重写里面的方法,然后给当前类加上@ControllerAdvice注解。实际应用时还有几个问题:问题一:重复打包问题二:ClassCastException: com.example.Spring_demo.Resp cannot be cast to java.lang.String统一异常处理需要使用两个注解@ExceptionHandler@ControllerAdvice

统一数据返回格式

统一数据返回格式就像我们寄快递一样,不管你需要寄的东西具体是什么都需要将它打包到统一的快递箱中。

此时我们需要一个“快递箱”用来将返回的数据“装”在里面。这个类是根据业务情况来自行定义的。

@Data
public class Resp<T> {
    //200-正常  0-发生异常
    private Integer code;
    //错误信息
    private String desc;
    //返回数据
    private T data;
    //成功
    public static <T> Resp<T> seccess(T data) {
        Resp<T> resp = new Resp<>();
        resp.setCode(200);
        resp.setData(data);
        return resp;
    }
}

image.gif

有了“快递箱之后”还需要一个“工作人员”来将“快递”进行打包,此时我们需要创建一个新类并使其实现ResponseBodyAdvice 接口并重写里面的方法,然后给当前类加上@ControllerAdvice注解。

@ControllerAdvice
public class Advice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return false;
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return null;
    }
}

image.gif

supports():这个方法用来决定是否执行beforeBodyWrite()方法,如果返回true表示执行,否则返回false。通过该方法可以选择哪些类或哪些方法的响应要进行处理,其他的不进行处理;beforeBodyWrite():对响应进行具体操作处理。

先创建两个用于测试的接口:

@RestController
@RequestMapping("/test")
public class Test {
    @RequestMapping("/fun1")
    public boolean fun1() {
        return false;
    }
    @RequestMapping("/fun2")
    public Integer fun2() {
        return 34;
    }
}

image.gif

image.gif

接下来将fun1和fun2方法的返回值进行“打包”。

@ControllerAdvice
public class Advice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return Resp.seccess(body);
    }
}

image.gif

image.gif

但是实际应用时还有几个问题:

问题一:重复打包

如果此时fun1返回的就是Resp类型的值那么就会出现重复打包的问题。

@RequestMapping("/fun1")
public Resp<Boolean> fun1() {
    return Resp.seccess(false);
}

image.gif

image.gif

解决方法就是可以在打包前进行一次判断,如果已经被打包了就直接返回,否则进行打包。

@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    if (body instanceof Resp) {
        return body;
    }
    return Resp.seccess(body);
}

image.gif

image.gif

问题二:ClassCastException: com.example.Spring_demo.Resp cannot be cast to java.lang.String

这个问题的出现场景是在返回值为String类型时出现的。假设fun1现在返回值是String类型:

@RequestMapping("/fun1")
public String fun1() {
    return "hahaha";
}

image.gif

image.gif

image.gif

解决方法就是如果返回结果为String类型, 使用SpringBoot内置提供的Jackson来实现信息的序列化。

@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    if (body instanceof String) {
        return new ObjectMapper().writeValueAsString(Resp.seccess(body));
    }
    if (body instanceof Resp) {
        return body;
    }
    return Resp.seccess(body);
}

image.gif

image.gif

注意:此时返回的是字符串。

使用这个就可以解决:@RequestMapping(value = "/fun1", produces = "application/json")

优点

  • 方便前端程序员更好的接收和解析后端数据接口返回的数据;
  • 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就可以了,因为所有接口都是这样返回的;
  • 有利于项目统一数据的维护和修改;
  • 有利于后端技术部门的统一规范的标准制定,不会出现稀奇古怪的返回内容。

统一异常处理

如果fun1方法在执行时出现异常也会出现不合理的返回信息:

@RequestMapping(value = "/fun1", produces = "application/json")
public String fun1() {
    System.out.println(3/0);
    return "hahaha";
}

image.gif

image.gif

此时就需要用到统一异常处理来对程序执行过程中发生的异常进行捕获和处理。统一异常处理需要使用两个注解@ExceptionHandler@ControllerAdvice,如果返回的是数据还需要加上@ResponseBody注解。

下面代码表示,如果代码出现Exception异常(包括Exception的子类)才会被捕获,然后执行该方法。

@ControllerAdvice
@ResponseBody
public class ErrorAdvice {
    @ExceptionHandler
    public Object handler(Exception e) {
        return Resp.fail("Exception: 程序出现异常");
    }
}

image.gif

image.gif

当有多个异常通知时,匹配顺序为当前类及其子类向上依次匹配。如此时有两个,继续访问fun1方法。

@ControllerAdvice
@ResponseBody
public class ErrorAdvice {
    @ExceptionHandler
    public Object handler(Exception e) {
        return Resp.fail("Exception: 程序出现异常");
    }
    @ExceptionHandler
    public Object handler(ArithmeticException e) {
        return Resp.fail("ArithmeticException: 程序出现算数异常");
    }
}

image.gif

image.gif

目录
相关文章
|
6月前
|
前端开发 Java Spring
统一异常处理
统一异常处理
46 2
|
3月前
|
JSON 前端开发 API
构建前端防腐策略问题之更新getMemoryUsagePercent函数以适应新的API返回格式的问题如何解决
构建前端防腐策略问题之更新getMemoryUsagePercent函数以适应新的API返回格式的问题如何解决
构建前端防腐策略问题之更新getMemoryUsagePercent函数以适应新的API返回格式的问题如何解决
|
5月前
|
前端开发 Java 程序员
SpringBoot统一功能处理,拦截器,统一数据格式,捕捉异常
SpringBoot统一功能处理,拦截器,统一数据格式,捕捉异常
|
6月前
|
前端开发 Java 程序员
Spring Boot统一功能处理(拦截器, 统一数据返回格式, 统一异常处理)
Spring Boot统一功能处理(拦截器, 统一数据返回格式, 统一异常处理)
110 1
|
6月前
|
前端开发
|
11月前
|
JSON Java 数据格式
SpringBoot 统一响应返回格式格式 数组
SpringBoot 统一响应返回格式格式 数组
|
JSON 前端开发 Java
统一的数据返回格式和异常处理
统一的数据返回格式和异常处理
303 0
统一的数据返回格式和异常处理
|
JSON 前端开发 Java
Spring Boot 统一参数校验、统一异常、统一响应,这才是优雅的处理方式!
Spring Boot 统一参数校验、统一异常、统一响应,这才是优雅的处理方式!
891 0
Spring Boot 统一参数校验、统一异常、统一响应,这才是优雅的处理方式!
统一结果返回
统一结果返回,可参考
72 0