目录
- 1 简介
- 2 快速入门
-
- 2.1 Spring Boot 接口开发现状
- 2.2 快速入门
1 简介
Graceful Response 是一个 Spring Boot 技术栈下的优雅响应处理器,提供一站式统一返回值封装、全局异常处理、自定义异常错误码 等功能,使用 Graceful Response 进行 web 接口开发不仅可以节省大量的时间,还可以提高代码质量,使代码逻辑更清晰。
强烈推荐你花 3 分钟学会它!
本项目案例工程代码:https://github.com/feiniaojin/graceful-response-example.git ,注意选择最新版本的分支。
| Spring Boot 版本 | Graceful Response 版本 | graceful-response-example 分支 |
|---|---|---|
| 2.x | 3.2.1-boot2 | 3.2.0-boot2 |
| 3.x | 3.2.1-boot3 | 3.2.0-boot3 |
注意,3.2.1-boot2 版本的 Graceful Response 源码由单独的仓库进行维护,地址为:https://github.com/feiniaojin/graceful-response-boot2
3.2.1-boot2 和 3.2.1-boot3 除了支持的 Spring Boot 版本不一样,其他实现完全一致,Maven 引用时只需要根据对应的 Spring Boot 版本选择 Graceful Response 的 version 即可,两者的 groupId、artifactId 是一致的。
2 快速入门
2.1 Spring Boot 接口开发现状
目前,业界使用 Spring Boot 进行接口开发时,往往存在效率底下、重复劳动、可读性差等问题。以下伪代码相信大家非常熟悉,我们大部分项目的 Controller 接口都是这样的。
1@Controller 2public class Controller { 3 4 @GetMapping("/query") 5 @ResponseBody 6 public Response query(Map<String, Object> paramMap) { 7 Response res = new Response(); 8 try { 9 //1.校验params参数合法性,包括非空校验、长度校验等 10 if (illegal(paramMap)) { 11 res.setCode(1); 12 res.setMsg("error"); 13 return res; 14 } 15 //2.调用Service的一系列操作,得到查询结果 16 Object data = service.query(params); 17 //3.将操作结果设置到res对象中 18 res.setData(data); 19 res.setCode(0); 20 res.setMsg("ok"); 21 return res; 22 } catch (Exception e) { 23 //4.异常处理:一堆丑陋的try...catch,如果有错误码的,还需要手工填充错误码 24 res.setCode(1); 25 res.setMsg("error"); 26 return res; 27 } 28 } 29} 30
这段伪代码存在什么样的问题呢?
第一个问题,效率低下。 Controller 层的代码应该尽量简洁,上面的伪代码其实只是为了将数据查询的结果进行封装,使其以统一的格式进行返回。例如以下格式的响应体:
1{ 2 "code": 0, 3 "msg": "ok", 4 "data": { 5 "id": 1, 6 "name": "username" 7 } 8} 9
查询过程中如果发生异常,需要在 Controller 进行手工捕获,根据捕获的异常人工地设置错误码,当然,也用同样的格式封装错误码进行返回。
可以看到,除了调用 service 层的 query 方法这一行,其他大部分的代码都执行进行结果的封装,大量的冗余、低价值的代码导致我们的开发活动效率很低。
第二个问题,重复劳动。 以上捕获异常、封装执行结果的操作,每个接口都会进行一次,因此造成大量重复劳动。
第三个问题,可读性低。 上面的核心代码被淹没在许多冗余代码中,很难阅读,如同大海捞针。
我们可以通过 Graceful Response 这个组件解决这样的问题。
2.2 快速入门
2.2.1 引入 Graceful Response 组件
Graceful Response 已发布至 maven 中央仓库,我们可以直接引入到项目中。
maven 依赖如下:
1<dependency> 2 <groupId>com.feiniaojin</groupId> 3 <artifactId>graceful-response</artifactId> 4 <version>{latest.version}</version> 5</dependency> 6
| Spring Boot 版本 | Graceful Response 最新版本 |
|---|---|
| 2.x | 3.2.1-boot2 |
| 3.x | 3.2.1-boot3 |
2.2.2 启用 Graceful Response
在启动类中引入 @EnableGracefulResponse 注解,即可启用 Graceful Response 组件。
1@EnableGracefulResponse 2@SpringBootApplication 3public class ExampleApplication { 4 public static void main(String[] args) { 5 SpringApplication.run(ExampleApplication.class, args); 6 } 7} 8
2.2.3 Controller 层
引入 Graceful Response 后,我们不需要再手工进行查询结果的封装,直接返回实际结果即可,Graceful Response 会自动完成封装的操作。
Controller 层示例如下。
1@Controller 2public class Controller { 3 @RequestMapping("/get") 4 @ResponseBody 5 public UserInfoView get(Long id) { 6 log.info("id={}", id); 7 return UserInfoView.builder().id(id).name("name" + id).build(); 8 } 9} 10
在示例代码中,Controller 层的方法直接返回了 UserInfoView 对象,没有进行封装的操作,但经过 Graceful Response 处理后,我们还是得到了以下的响应结果。
1{ 2 "status": { 3 "code": "0", 4 "msg": "ok" 5 }, 6 "payload": { 7 "id": 1, 8 "name": "name1" 9 } 10} 11
而对于命令操作(Command)尽量不返回数据,因此 command 操作的方法的返回值应该是 void,Graceful Response 对于对于返回值类型 void 的方法,也会自动进行封装。
1public class Controller { 2 @RequestMapping("/command") 3 @ResponseBody 4 public void command() { 5 //业务操作 6 } 7} 8
成功调用该接口,将得到:
1{ 2 "status": { 3 "code": "200", 4 "msg": "success" 5 }, 6 "payload": {} 7} 8
2.2.4 Service 层
在引入 Graceful Response 前,有的开发者在定义 Service 层的方法时,为了在接口中返回异常码,干脆直接将 Service 层方法定义为 Response,淹没了方法的正常返回值。
Response 的代码如下。
1//lombok注解 2@Data 3public class Response { 4 private String code; 5 private String msg; 6 private Object data; 7} 8
直接返回 Response 的 Service 层方法:
1/** 2 * 直接返回Reponse的Service 3 * 不规范 4 */ 5public interface Service { 6 public Reponse commandMethod(Command command); 7} 8
Graceful Response 引入 @ExceptionMapper 注解,通过该注解将异常和错误码关联起来,这样 Service 方法就不需要再维护 Response 的响应码了,直接抛出业务异常,由 Graceful Response 进行异常和响应码的关联。
@ExceptionMapper 的用法如下。
1/** 2 * NotFoundException的定义,使用@ExceptionMapper注解修饰 3 * code:代表接口的异常码 4 * msg:代表接口的异常提示 5 */ 6@ExceptionMapper(code = "1404", msg = "找不到对象") 7public class NotFoundException extends RuntimeException { 8 9} 10
Service 接口定义:
1public interface QueryService { 2 UserInfoView queryOne(Query query); 3} 4
Service 接口实现:
1public class QueryServiceImpl implements QueryService { 2 @Resource 3 private UserInfoMapper mapper; 4 5 public UserInfoView queryOne(Query query) { 6 UserInfo userInfo = mapper.findOne(query.getId()); 7 if (Objects.isNull(userInfo)) { 8 //这里直接抛自定义异常 9 throw new NotFoundException(); 10 } 11 //……后续业务操作 12 } 13} 14
当 Service 层的 queryOne 方法抛出 NotFoundException 时,Graceful Response 会进行异常捕获,并将 NotFoundException 对应的异常码和异常信息封装到统一的响应对象中,最终接口返回以下 JSON。
1{ 2 "status": { 3 "code": "1404", 4 "msg": "找不到对象" 5 }, 6 "payload": {} 7} 8
2.2.5 参数校验
Graceful Response 对 JSR-303 数据校验规范和 Hibernate Validator 进行了增强,Graceful Response 自身不提供参数校验的功能,但是用户使用了 Hibernate Validator 后,Graceful Response 可以通过 @ValidationStatusCode 注解为参数校验结果提供响应码,并将其统一封装返回。
例如以下的 UserInfoQuery。
1@Data 2public class UserInfoQuery { 3 @NotNull(message = "userName is null !") 4 @Length(min = 6, max = 12) 5 @ValidationStatusCode(code = "520") 6 private String userName; 7} 8
UserInfoQuery 对象中定义了 @NotNull 和 @Length 两个校验规则,在未引入 Graceful Response 的情况下,会直接抛出异常;
在引入 Graceful Response 但是没有加入 @ValidationStatusCode 注解的情况下,会以默认的错误码进行返回;
在上面的 UserInfoQuery 中由于使用了 @ValidationStatusCode 注解,并指定异常码为 520,则当 userName 字段任意校验不通过时,都会使用异常码 520 进行返回,如下。
1{ 2 "status": { 3 "code": "520", 4 "msg": "userName is null !" 5 }, 6 "payload": {} 7} 8
而对于 Controller 层直接校验方法入参的场景,Graceful Response 也进行了增强,如以下 Controller。
1public class Controller { 2 3 @RequestMapping("/validateMethodParam") 4 @ResponseBody 5 @ValidationStatusCode(code = "1314") 6 public void validateMethodParam( 7 @NotNull(message = "userId不能为空") Long userId, 8 @NotNull(message = "userName不能为空") Long userName) { 9 //省略业务逻辑 10 } 11} 12
如果该方法入参校验触发了 userId 和 userName 的校验异常,将以错误码 1314 进行返回,如下。
1{ 2 "status": { 3 "code": "1314", 4 "msg": "userId不能为空" 5 }, 6 "payload": {} 7} 8
2.2.6 自定义 Response 格式
Graceful Response 内置了两种风格的响应格式,并通过 graceful-response.response-style 进行配置。
graceful-response.response-style=0,或者不配置(默认情况),将以以下的格式进行返回:
1{ 2 "status": { 3 "code": 1007, 4 "msg": "有内鬼,终止交易" 5 }, 6 "payload": { 7 } 8} 9
graceful-response.response-style=1,将以以下的格式进行返回:
1{ 2 "code": "1404", 3 "msg": "not found", 4 "data": { 5 } 6} 7
如果这两种格式均不满足业务需要,Graceful Response 也支持用户自定义响应体,关于自定义响应体的技术实现,请到自定义 Response 格式进行了解。
本项目提供的进阶功能,包括
- 第三方组件汽车(Swagger、执行器等)
- 自定义响应
- 异常请求放行
- 异常别名
- 常用配置项
目前该组件在 GitHub 上已经有两百多 Star,很多朋友已经开始用了,大家可以通过下方链接了解下: