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

统一数据返回格式

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

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

java 复制代码
@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;
    }
}

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

java 复制代码
@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;
    }
}

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

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

java 复制代码
@RestController
@RequestMapping("/test")
public class Test {

    @RequestMapping("/fun1")
    public boolean fun1() {
        return false;
    }

    @RequestMapping("/fun2")
    public Integer fun2() {
        return 34;
    }
}

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

java 复制代码
@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);
    }
}

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

问题一:重复打包

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

java 复制代码
@RequestMapping("/fun1")
public Resp<Boolean> fun1() {
    return Resp.seccess(false);
}

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

java 复制代码
@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);
}

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

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

java 复制代码
@RequestMapping("/fun1")
public String fun1() {
    return "hahaha";
}

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

java 复制代码
@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);
}

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

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

优点

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

统一异常处理

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

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

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

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

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

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

java 复制代码
@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: 程序出现算数异常");
    }
}
相关推荐
P.H. Infinity2 分钟前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天6 分钟前
java的threadlocal为何内存泄漏
java
caridle17 分钟前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
萧鼎20 分钟前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸21 分钟前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农22 分钟前
Python 继承、多态、封装、抽象
开发语言·python
^velpro^22 分钟前
数据库连接池的创建
java·开发语言·数据库
苹果醋326 分钟前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花31 分钟前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端33 分钟前
第六章 7.0 LinkList
java·开发语言·网络