一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应处理器

作者:京东物流 覃玉杰

1. 简介

Graceful Response是一个Spring Boot体系下的优雅响应处理器,提供一站式统一返回值封装、异常处理、异常错误码等功能。

使用Graceful Response进行web接口开发不仅可以节省大量的时间,还可以提高代码质量,使代码逻辑更清晰。

强烈推荐你花3分钟学会它!

Graceful Response的Github地址: https://github.com/feiniaojin/graceful-response ,欢迎star!

Graceful Response的案例工程代码:https://github.com/feiniaojin/graceful-response-example.git

2. Spring Boot Web API接口数据返回的现状

我们进行Spring Boo Web API接口开发时,通常大部分的Controller代码是这样的:

public class Controller {
    @GetMapping("/query")
    @ResponseBody
    public Response query(Parameter params) {

        Response res = new Response();
        try {
            //1.校验params参数,非空校验、长度校验
            if (illegal(params)) {
                res.setCode(1);
                res.setMsg("error");
                return res;
            }
            //2.调用Service的一系列操作
            Data data = service.query(params);
            //3.执行正确时,将操作结果设置到res对象中
            res.setData(data);
            res.setCode(0);
            res.setMsg("ok");
            return res;
        } catch (BizException1 e) {
            //4.异常处理:一堆丑陋的try...catch,如果有错误码的,还需要手工填充错误码
            res.setCode(1024);
            res.setMsg("error");
            return res;
        } catch (BizException2 e) {
            //4.异常处理:一堆丑陋的try...catch,如果有错误码的,还需要手工填充错误码
            res.setCode(2048);
            res.setMsg("error");
            return res;
        } catch (Exception e) {
            //4.异常处理:一堆丑陋的try...catch,如果有错误码的,还需要手工填充错误码
            res.setCode(1);
            res.setMsg("error");
            return res;
        }
    }
}

这段代码存在什么问题呢?真正的业务逻辑被冗余代码淹没,可读性太差。

真正执行业务的代码只有

Data data=service.query(params);

其他代码不管是正常执行还是异常处理,都是为了异常封装、把结果封装为特定的格式,例如以下格式:

{
  "code": 0,
  "msg": "ok",
  "data": {
    "id": 1,
    "name": "username"
  }
}

这样的逻辑每个接口都需要处理一遍,都是繁琐的重复劳动。

现在,只需要引入Graceful Response组件并通过@EnableGracefulResponse启用,就可以直接返回业务结果并自动完成response的格式封装。

以下是使用Graceful Response之后的代码,实现同样的返回值封装、异常处理、异常错误码功能,但可以看到代码变得非常简洁,可读性非常强。

public class Controller {
    @GetMapping("/query")
    @ResponseBody
    public Data query(Parameter params) {
       return service.query(params);
    }
}

3. 快速入门

3.1 引入maven依赖

graceful-response已发布至maven中央仓库,可以直接引入到项目中,maven依赖如下:

<dependency>
    <groupId>com.feiniaojin</groupId>
    <artifactId>graceful-response</artifactId>
    <version>2.0</version>
</dependency>

3.2 在启动类中引入@EnableGracefulResponse注解

@EnableGracefulResponse
@SpringBootApplication
public class ExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }
}

3.3 Controller方法直接返回结果

• 普通的查询

@Controller
public class Controller {
    @RequestMapping("/get")
    @ResponseBody
    public UserInfoView get(Long id) {
        log.info("id={}", id);
        return UserInfoView.builder().id(id).name("name" + id).build();
    }
}

UserInfoView的源码:

@Data
@Builder
public class UserInfoView {
    private Long id;
    private String name;
}

这个接口直接返回了 UserInfoView的实例对象,调用接口时,Graceful Response将自动封装为以下格式:

{
  "status": {
    "code": "0",
    "msg": "ok"
  },
  "payload": {
    "id": 1,
    "name": "name1"
  }
}

可以看到UserInfoView被自动封装到payload字段中。

Graceful Response提供了两种风格的Response,可以通过在application.properties文件中配置gr.responseStyle=1,将以以下的格式进行返回:

{
  "code": "0",
  "msg": "ok",
  "data": {
    "id": 1,
    "name": "name1"
  }
}

如果这两种风格也不能满足需要,我们还可以根据自己的需要进行自定义返回的Response格式。详细见本文 4.3自定义Respnse格式。

• 异常处理的场景

通过Graceful Response,我们不需要专门在Controller中处理异常,详细见 4.1 Graceful Response异常错误码处理。

• 返回值为空的场景

某些Command类型的方法只执行修改操作,不返回数据,这个时候我们可以直接在Controller中返回void,Graceful Response会自动封装默认的操作成功Response报文。

@Controller
public class Controller {
    @RequestMapping("/void")
    @ResponseBody
    public void testVoidResponse() {
        //省略业务操作
    }
}

testVoidResponse方法的返回时void,调用这个接口时,将返回:

{
  "status": {
    "code": "200",
    "msg": "success"
  },
  "payload": {}
}

3.4 Service方法业务处理

在引入Graceful Response后,Service层的方法的可读性可以得到极大的提升。

• 接口直接返回业务数据类型,而不是Response,更具备可读性

public interface ExampleService {
    UserInfoView query1(Query query);
}

• Service接口实现类中,直接抛自定义的业务异常,Graceful Response将其转化为返回错误码和错误提示

public class ExampleServiceImpl implements ExampleService {
    @Resource
    private UserInfoMapper mapper;

    public UserInfoView query1(Query query) {
        UserInfo userInfo = mapper.findOne(query.getId());
        if (Objects.isNull(userInfo)) {
           //这里直接抛自定义异常,异常通过@ExceptionMapper修饰,提供异常码和异常提示
           throw new NotFoundException();
        }
        // 省略后续业务操作
    }
}


/**
 * NotFoundException的定义,使用@ExceptionMapper注解修饰
 * code:代表接口的异常码
 * msg:代表接口的异常提示
 */
@ExceptionMapper(code = "1404", msg = "找不到对象")
public class NotFoundException extends RuntimeException {

}
//Controller不再捕获处理异常
@RequestMapping("/get")
@ResponseBody
public UserInfoView get(Query query)) {
    return exampleService.query1(query);
}

当Service方法抛出NotFoundException异常时,接口将直接返回错误码,不需要手工set,极大地简化了异常处理逻辑。

{
  "status": {
    "code": "1404",
    "msg": "找不到对象"
  },
  "payload": {}
}

验证:启动example工程后,请求http://localhost:9090/example/notfound

4. 进阶用法

4.1 Graceful Response异常错误码处理

以下是使用Graceful Response进行异常、错误码处理的开发步骤。

• 创建自定义异常

通过继承RuntimeException类创建自定义的异常,采用 @ExceptionMapper注解修饰,注解的 code属性为返回码,msg属性为错误提示信息。

关于是继承RuntimeException还是继承Exception,读者可以根据实际情况去选择,Graceful Response对两者都支持。

@ExceptionMapper(code = "1007", msg = "有内鬼,终止交易")
public static final class RatException extends RuntimeException {

}

• Service执行具体逻辑

Service执行业务逻辑的过程中,需要抛异常的时候直接抛出去即可。由于已经通过@ExceptionMapper定义了该异常的错误码,我们不需要再单独的维护异常码枚举与异常类的关系。

//Service层伪代码
public class Service {
    public void illegalTransaction() {
        //需要抛异常的时候直接抛
        if (check()) {
            throw new RatException();
        }
        doIllegalTransaction();
    }
}

Controller层调用Service层伪代码:

public class Controller {
    @RequestMapping("/test3")
    public void test3() {
        //Controller中不会进行异常处理,也不会手工set错误码,只关心核心操作,其他的统统交给Graceful Response
        exampleService.illegalTransaction();
    }
}

在浏览器中请求controller的/test3方法,有异常时将会返回:

{
  "status": {
    "code": "1007",
    "msg": "有内鬼,终止交易"
  },
  "payload": {
  }
}

4.2 外部异常别名

案例工程( https://github.com/feiniaojin/graceful-response-example.git )启动后, 通过浏览器访问一个不存在的接口,例如 http://localhost:9090/example/get2?id=1

如果没开启Graceful Response,将会跳转到404页面,主要原因是应用内部产生了 NoHandlerFoundException异常。如果开启了Graceful Response,默认会返回code=1的错误码。

这类非自定义的异常,如果需要自定义一个错误码返回,将不得不对每个异常编写Advice逻辑,在Advice中设置错误码和提示信息,这样做也非常繁琐。

Graceful Response可以非常轻松地解决给这类外部异常定义错误码和提示信息的问题。

以下为操作步骤:

• 创建异常别名,并用 @ExceptionAliasFor注解修饰

@ExceptionAliasFor(code = "1404", msg = "Not Found", aliasFor = NoHandlerFoundException.class)
public class NotFoundException extends RuntimeException {
}

code:捕获异常时返回的错误码

msg:异常提示信息

aliasFor:表示将成为哪个异常的别名,通过这个属性关联到对应异常。

• 注册异常别名

创建一个继承了AbstractExceptionAliasRegisterConfig的配置类,在实现的registerAlias方法中进行注册。

@Configuration
public class GracefulResponseConfig extends AbstractExceptionAliasRegisterConfig {

    @Override
    protected void registerAlias(ExceptionAliasRegister aliasRegister) {
        aliasRegister.doRegisterExceptionAlias(NotFoundException.class);
    }
}

• 浏览器访问不存在的URL

再次访问 http://localhost:9090/example/get2?id=1 ,服务端将返回以下json,正是在ExceptionAliasFor中定义的内容

{
  "code": "1404",
  "msg": "not found",
  "data": {
  }
}

4.3 自定义Response格式

Graceful Response内置了两种风格的响应格式,可以在application.properties文件中通过gr.responseStyle进行配置

• gr.responseStyle=0,或者不配置(默认情况)

将以以下的格式进行返回:

{
  "status": {
    "code": "1007",
    "msg": "有内鬼,终止交易"
  },
  "payload": {
  }
}

• gr.responseStyle=1

将以以下的格式进行返回:

{
  "code": "1404",
  "msg": "not found",
  "data": {
  }
}

• 自定义响应格式

如果以上两种格式均不能满足业务需要,可以通过自定义去满足,Response

例如以下响应:

public class CustomResponseImpl implements Response {

    private String code;

    private Long timestamp = System.currentTimeMillis();

    private String msg;

    private Object data = Collections.EMPTY_MAP;

    @Override
    public void setStatus(ResponseStatus statusLine) {
        this.code = statusLine.getCode();
        this.msg = statusLine.getMsg();
    }

    @Override
    @JsonIgnore
    public ResponseStatus getStatus() {
        return null;
    }

    @Override
    public void setPayload(Object payload) {
        this.data = payload;
    }

    @Override
    @JsonIgnore
    public Object getPayload() {
        return null;
    }

    public String getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Long getTimestamp() {
        return timestamp;
    }
}

注意,不需要返回的属性可以返回null或者加上@JsonIgnore注解

• 配置gr.responseClassFullName

将CustomResponseImpl的全限定名配置到gr.responseClassFullName属性。

gr.responseClassFullName=com.feiniaojin.gracefuresponse.example.config.CustomResponseImpl

注意,配置gr.responseClassFullName后,gr.responseStyle将不再生效。

实际的响应报文如下:

{
    "code":"200",
    "timestamp":1682489591319,
    "msg":"success",
    "data":{

    }
}

如果还是不能满足需求,那么可以考虑同时自定义实现Response和ResponseFactory这两个接口。

5. 常用配置

Graceful Response在版本迭代中,根据用户反馈提供了一些常用的配置项,列举如下:

• gr.printExceptionInGlobalAdvice是否打印异常日志,默认为false

• gr.responseClassFullName自定义Response类的全限定名,默认为空。 配置gr.responseClassFullName后,gr.responseStyle将不再生效

• gr.responseStyleResponse风格,不配置默认为0

• gr.defaultSuccessCode自定义的成功响应码,不配置则为0

• gr.defaultSuccessMsg自定义的成功提示,默认为ok

• gr.defaultFailCode自定义的失败响应码,默认为1

• gr.defaultFailMsg自定义的失败提示,默认为error

原文链接:https://www.cnblogs.com/jingdongkeji/p/17384006.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应处理器 - Python技术站

(0)
上一篇 2023年5月9日
下一篇 2023年5月9日

相关文章

  • spring boot与ktor整合的实现方法

    下面我将详细讲解“Spring Boot与Ktor整合的实现方法”的完整攻略,并提供两个示例。 Spring Boot与Ktor整合 1. 环境准备 在开始整合前,需要准备好以下环境: JDK 8以上版本 Gradle 4以上版本 Spring Boot 2以上版本 Ktor 1以上版本 2. Spring Boot项目搭建 首先,需要新建一个Spring …

    Java 2023年5月19日
    00
  • Java对象转换的方案分享

    下面就给大家详细讲解一下Java对象转换的方案分享,内容主要包括以下几个方面: 为什么需要Java对象转换 常见的Java对象转换方式和工具 示例说明:使用Jackson工具进行对象转换 示例说明:手动编写代码进行对象转换 1. 为什么需要Java对象转换 Java中的对象通常有很多种类型,比如字符串、数字、日期、自定义对象等等。在编程的过程中,我们可能需要…

    Java 2023年5月26日
    00
  • java中mybatis和hibernate的用法总结

    Java中MyBatis和Hibernate的用法总结 1. MyBatis的用法示例 1.1. 配置MyBatis数据源 在MyBatis中使用数据源需要在项目的配置文件mybatis-config.xml中进行配置。下面以配置MySQL连接为例进行说明。 <!– 配置数据源 –> <dataSource type="POO…

    Java 2023年5月20日
    00
  • 快速解决Tomcat重新配置后启动慢的问题

    下面是详细讲解如何快速解决Tomcat重新配置后启动慢的问题的完整攻略: 问题描述 Tomcat在重新进行配置后,启动时间变得非常慢,甚至在启动时停滞数分钟,这可能会严重打乱开发时间表。 原因分析 多种原因可能导致Tomcat出现这种情况。其中最常见的原因是服务器需要在Web应用程序启动时加载所有的类和配置信息,这些信息都存储在本地文件系统上,这些操作会消耗…

    Java 2023年5月19日
    00
  • java的Hibernate框架报错“StaleObjectStateException”的原因和解决方法

    当使用Java的Hibernate框架时,可能会遇到“StaleObjectStateException”错误。这个错误通常是由于以下原因之一引起的: 数据库中的数据已被修改:如果您的数据已被修改,则可能会出现此错误。在这种情况下,需要检查您的数据以解决此问题。 并发访问:如果多个用户同时访问同一数据,则可能会出现此错误。在这种情况下,需要使用乐观锁或悲观锁…

    Java 2023年5月4日
    00
  • 关于Java 中 Future 的 get 方法超时问题

    关于Java中Future的get方法超时问题 在Java中,Future是一种用于异步处理结果的接口。我们可以通过Future来获取异步函数执行结果,但是在使用Future的时候,如果异步函数长时间没有返回结果,就有可能引起get方法超时的问题。下面来详细讲解如何避免这个问题: 超时时间设置 在使用get()方法获取Future结果时,我们可以使用带超时时…

    Java 2023年5月25日
    00
  • 小程序server请求微信服务器超时的解决方法

    针对“小程序server请求微信服务器超时的解决方法”,我准备了以下完整攻略: 1.问题描述 在开发小程序时,经常会因为网络原因导致server请求微信服务器超时,这会影响用户体验,因此需要进行相应的处理。 2.可能的原因 网络不稳定导致请求超时。 请求量大,服务器压力过大。 微信服务器故障。 3.解决方法 对于请求超时的问题,我们可以采取以下一些措施: 3…

    Java 2023年5月23日
    00
  • Java私有构造函数作用原理解析

    Java私有构造函数作用原理解析 在Java面向对象编程中,构造函数是非常基础的概念。一个类中的构造函数可以用来进行初始化操作,并且在创建new对象时被自动调用。然而,在某些情况下,我们需要禁止创建对象或者限制创建对象的种类,这时候可以使用私有构造函数。本文将详细解析私有构造函数的作用原理和使用技巧。 一、什么是私有构造函数 首先,来看一下如何定义私有构造函…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部