首页 > 其他分享 >SpringBoot项目统一处理返回值和异常

SpringBoot项目统一处理返回值和异常

时间:2023-08-16 22:34:56浏览次数:35  
标签:code SpringBoot result 返回值 message 异常 public Result

目录

简介

当使用SpringBoot开发Web项目的API时,为了与前端更好地通信,通常会约定好接口的响应格式。例如,以下是一个JSON格式的响应,通过返回码和返回信息告知前端具体的操作结果或错误信息。如果操作成功,前端可以通过"data"字段获取响应内容。

{
    "code":"000000",
    "message":"操作成功",
    "data": true
}

如果所有接口都这样一个一个的封,那开发人员估计就先一个一个的疯了,为了减少手动的处理过程,让开发人员可以专注于业务本身,本文将向你展示一种非常优雅的方式。

除了统一处理接口的响应内容以外,在一般的业务流程中,不管是自定义的异常还是因为各种问题而抛出的异常,在前端接收到的接口响应状态都是500错误,接口响应的内容上也非常不友好,如下图。

image
而实际的业务中,我们更希望可以给用户一个相对友好的提示,之前展示了一个成功的响应,这里我们也可以将异常情况处理为同样的结构,例如:

{
    "code":"010101",
    "message":"订单当前不支持此操作",
    "data": ""
}

这样也就可以让前端通过特定的方式将异常信息以更友好的方式展示给用户,而不是干巴巴的代码,接下来我们废话不多说,开干!!!

前期准备

首先我们需要准备一个示例工程用于实操演示,这里给大家准备了一个项目并开源到了Gitee,大家可以执行以下命令获取当前需要使用的示例项目,就是最基础的SpringBoot项目,大家也可以自己初始化一个,这个项目后续我会继续往里添加功能,大家可以关注下,说不定什么时候就可以用上了,也欢迎大家通过各种方式与我沟通相关问题。

git clone https://gitee.com/itartisans/itartisans-framework.git
git checkout base-springboot-web

统一封装报文

为了统一处理业务代码返回的逻辑,我们需要准备一个实体类,核心代码就是以下属性,分别对应着开头的报文各个属性,而其他的getter和setter方法则因为篇幅原因省略了。

public class Result {
    private String code;
    private String message;
    private Object data;
    // todo 添加的省略getter和setter方法
}

报文的统一处理需要依赖Spring提供的AOP支持,Spring官方针对响应内容的处理提供了ResponseBodyAdvice接口,对响应报文的封装则需要实现此接口。

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {

    @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) {
        Result result = new Result();
        result.setCode("000000");
        result.setMessage("操作成功");
        result.setData(body);
        return result;
    }
}

说明:

  • 第一行的@ControllerAdvice声明此类是一个切片处理类;
  • supports方法用于标识切面的覆盖范围,如果返回true则会执行beforeBodyWrite方法里的代码,如果返回false则直接响应;
  • beforeBodyWrite方法用于对响应值进行自定义操作,此处通过Result进行封装;
    完成以上处理后,我们可以创建个Controller请求下看看返回结果,如果示例代码是返回的不是String类型的话,那么应该是可以正常响应的,但是如果你尝试的是String,你就会收到这么一个异常:
class org.itartisans.framework.model.entity.Result cannot be cast to class java.lang.String

收到这个异常的原因是Spring默认使用的序列化器是StringHttpMessageConverter,这个序列化器无法正常的将对象转换为String类型,需要手动指定使用MappingJackson2HttpMessageConverter进行处理,而指定序列化处理器则需要实现WebMvcConfigurer并通过重写configureMessageConverters方法实现,具体代码如下:

@Configuration
public class WebConfiguration implements WebMvcConfigurer {
   
     @Override    
     public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0, new MappingJackson2HttpMessageConverter());
    }
}

完成序列化处理器的配置后即可愉快的测试所有类型的返回值了。

统一异常处理

在完成以上的报文封装后,如果代码执行过程中出现了异常,则会出现以下内容,此处是手动throw异常模拟的情况。

{
    "code": "000000",
    "message": "操作成功",
    "data": {
        "timestamp": 1691503634161,
        "status": 500,
        "error": "Internal Server Error",
        "path": "/demo/getObject"
    }
}

报文即使在失败时也显示了操作成功,这明显不是正常的情况,也不符合我们最初可以定制错误码和错误信息的需求,统一处理异常同样需要使用AOP,创建一个针对异常的切面处理器,代码如下:

@RestControllerAdvice
public class ExceptionAdvice {
    
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) {
        Result result = new Result();
        result.setCode("999999");
        result.setMessage("操作失败");
        return result;
    }
    
}

这部分代码和封装报文时有共同指出,这里介绍下不一样的地方:

  • 第一行的@RestControllerAdvice与@RestController类似,表示此类中的方法返回值均为JSON类型
  • 第四行的@ExceptionHandler指定该方法时一个异常处理器,参数对应处理的异常,这里指定的Exception.class表示处理所有异常

经过切面处理后报文如下,可以返现data中的返回内容正是我们想要的响应报文。

{
    "code": "000000",
    "message": "操作成功",
    "data": {
        "code": "999999",
        "message": "操作失败",
        "data": null
    }
}

这里则是因为上一步统一封装报文时我们没有对方法的返回值进行区分,一刀切的对所有返回值进行了封装,结果出现了这种情况。既然知道了问题所在,那就好解决了,在封装报文时我们知道ResponseAdvice中的supports方法是用于判断是否执行beforeBodyWrite的,那我们只需要在supports中判断返回值类型是否为Result即可,如果不是才进行封装,如果是则直接返回。代码如下:

@Override
public boolean supports(MethodParameter returnType, Class converterType) {
    return !returnType.getParameterType().equals(Result.class);
}

修改后重启应用再次请求接口即可发现报文格式符合我们预期了。

{
    "code": "999999",
    "message": "操作失败",
    "data": null
}

自定义异常信息

到此为止我们完成了异常的统一处理,但是目前所有的异常都会返回999999,无法进行自定义错误内容,显然还没有填完开头挖的坑,所以我们继续对程序进行改造,在介绍@ExceptionHandler我们提到参数里可以指定处理的异常类型,那我们就从此处入手,自定义一个异常,然后增加一个对应的异常处理器,在异常处理器中根据异常中的错误信息进行转换。

因为要自定义异常,不可能将异常信息散落在代码里,所以要选择一个地方统一维护异常代码和对应的异常信息,这里作者选择的是枚举类,如果项目有其他要求也可以选择properties文件等载体,只是读取信息时方式不一样而已。枚举类代码如下,添加此枚举之后大家也可以把之前涉及SUCCESS和FAILED的地方重构一下,这里就不展开了:

public enum ResultCode {

    SUCCESS("000000", "操作成功"),
    FAILED("999999", "操作失败"),
    DEMO_ERROR("010101", "订单当前不支持此操作")
    ;

    private final String code;
    private final String message;

    ResultCode(String code, String message) {
        this.code = code;
        this.message = message;
    }
    // TODO 添加getter和setter方法
}

有了异常信息列表后我们需要创建一个自定义异常作为信息载体,以便可以在代码中使用throw触发异常处理器,代码如下:

public class FrameworkException extends Exception{

    private ResultCode resultCode;

    public FrameworkException(ResultCode resultCode) {
        super(resultCode.getMessage() + "(" + resultCode.getCode() + ")");
        this.resultCode = resultCode;
    }
    // TODO 添加getter和setter方法
}

之后我们要添加对应的异常处理器,在ExceptionAdvice中增加如下方法,用于处理我们刚刚添加的自定义异常。

@ExceptionHandler(FrameworkException.class)
public Result handleFrameworkException(FrameworkException e) {
    ResultCode code = e.getResultCode();
    Result result = new Result();
    result.setCode(code.getCode());
    result.setMessage(code.getMessage());
    return result;
}

之后我们在测试方法中任意位置添加如下测试代码,注意添加之后之前的代码会报错,因为异常后的代码无法执行到会导致编译报错,这里注释掉即可。

throw new FrameworkException(ResultCode.DEMO_ERROR);

更新后重启项目再次请求接口即可看到已经完成了异常信息的自定义,前端也可以针对性的进行业务处理了。完结!撒花~

{
    "code": "010101",
    "message": "订单当前不支持此操作",
    "data": null
}

标签:code,SpringBoot,result,返回值,message,异常,public,Result
From: https://www.cnblogs.com/itartisans/p/17621796.html

相关文章

  • SpringBoot3.x 启动 refresh 过程解析
    Spring容器创建后,会调用它的refresh方法,refresh的时候会做很多事情:如完成配置类解析、各种BeanFactoryPostProcessor和BeanPostProcessor的注册、国际化配置的初始化、web内置容器的构造等等。web程序对应Spring容器为AnnotationConfigServletWebServerApplicationContext。Servlet......
  • 限制程序重复启动+全局异常捕获
    限制程序重复启动:boolnoAny;System.Threading.Mutexm=newSystem.Threading.Mutex(true,Application.ProductName,outnoAny);if(!noAny){MessageBox.Show("程序已在运行,禁止重复启动!","提示");System.Threading.Thread.Sleep(500);Environment.......
  • ThingsKit物联网平台异常日志管理
    记录平台的异常日志,例如:功能异常信息、平台报错等等。文章来源(首发地址):ThingsKit物联网平台......
  • 拉去springboot 项目时java8 本地时java11 时idea编译不通过
    解决方法:1、 2、 3、 ......
  • 基于springboot度假村管理系统
    随着互联网技术和信息化技术的不断深入发展,利用互联网技术进行信息化管理有了很大的提高,从而使得信息管理变的越来越快捷。面对互联网的发展提升引发的新的管理方式,度假村管理人员急需要一套管理系统来规范度假村各项信息的自动化。通过该系统度假村管理人员都能够做到度假村信息的......
  • 【上传文件时异常】The field file exceeds its maximum permitted size of 1048576 b
    1、背景描述本项目是个springboot项目,需要文件上传,上传的是一个pdf文件,大小是5MB,报错内容如下:Causedby:org.apache.tomcat.util.http.fileupload.FileUploadBase$FileSizeLimitExceededException:Thefieldfileexceedsitsmaximumpermittedsizeof1048576bytes.2......
  • 基于springboot物资类运维任务管理系统
    本课题重点主要完成了对于Springboot框架的线上物资运维系统的需求分析、开发、设计和测试。完整的系统主要包含了物料信息、采购申请、入库申请、出库信息、员工信息这几大模块,满足了用户在线物资运维的需求,提高了物资行业的信息化水平。技术方案如下:通过在myeclipse的平台上去开......
  • Spark异常总结
    1、Spark读写同一张表报错问题Cannotoverwriteapaththatisalsobeingreadfrom问题描述:SparkSQL在执行ORC和Parquet格式的文件解析时,默认使用Spark内置的解析器(Spark内置解析器效率更高),这些内置解析器不支持递归子目录的两项参数。可以通过设置spark.sql.hive.convertMe......
  • JavaSE--异常处理机制
    一、异常exception1、什么是异常,以及Java提供的异常处理机制  程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常  java语言提供了异常的处理方式,如果程序执行过程中出现了不正常情况,  java把该异常信息打印输出到控制台,供程序员参考。程序员看到异常信息之......
  • java的异常处理机制
    Java的异常处理机制是一种用于处理程序运行中出现的异常情况的机制。当程序发生异常时,如果没有适当地处理异常,程序可能会终止或导致不可预测的结果。Java的异常处理机制提供了一种结构化的方式来捕获和处理异常,以便程序可以优雅地处理异常情况。Java的异常处理机制基于以下几个关......