Graceful Response是笔者开源的一个Spring Boot体系下接口响应处理器,用极致优雅的方式提供了全局异常处理、统一结果封装、外部异常映射错误码等功能。
Graceful Response使用非常简单,毫无学习难度,建议大家花一分钟学会之后应用到我们的项目中。
源码地址:https://github.com/feiniaojin/graceful-response.git。
案例源码地址:https://github.com/feiniaojin/graceful-response-example.git。
欢迎大家star。
通常我们进行Java Web API接口时,大部分的Controller代码是这样的:
@GetMapping@ResponseBodypublic 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" }}或者
{ "status": { "code": 0, "msg": "ok" }, "payload": { "id": 1, "name": "username" }}而且,这样的逻辑每个接口都需要处理一遍,都是繁琐的重复劳动。
现在,在引入Graceful Response组件后,我们只要直接返回业务结果,Graceful Response即可自动完成封装。
以下是一个简单的案例。
接口Controller代码:
@RequestMapping("/get")@ResponseBodypublic UserInfoView get(Long id) { log.info("id=" + id); return UserInfoView.builder().id(id).name("name" + id).build();}这个接口我们直接返回了UserInfoView的实例对象,并没有封装到Response中,但是我们调用接口时,返回的却是按照为以下格式封装好的响应:
{ "status":{ "code":"0", "msg":"ok" }, "payload":{ "id":1, "name":"name1" }}我们的返回结果被自动封装到payload字段中。
注:返回结果的格式是可以自定义的,以上的格式只是作者习惯采用的,我们可以根据自己的需要进行自定义返回的Response格式。
在本组件的案例工程( https://github.com/feiniaojin/graceful-response-example.git )中,
提供了封装为以下格式的ResponseFactory,详细见CustomResponseFactoryImpl。
graceful-response-example中自定义的返回值格式:
{ "code":"0", "msg":"ok", "data":{ "id":1, "name":"name1" }}在上面的示例代码中可以看到,为了根据不同的异常返回不同的错误码,捕获了多个异常,并且手工set错误码。
try{ //省略业务操作代码…… }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; }我们可以通过Graceful Response优化这个过程:直接抛异常,Graceful Response捕获异常、封装Response并set错误码和提示信息。
以下是使用Graceful Response进行异常、错误码处理的开发步骤。
(1)创建自定义异常,采用@ExceptionMapper注解修饰,注解的code属性为返回码,msg属性为错误提示信息
@ExceptionMapper(code = 1007, msg = "有内鬼,终止交易")public static final class RatException extends RuntimeException {}(2)service执行具体逻辑,需要抛异常的时候直接抛出去即可,不需要再关心异常与错误码关联的问题
public void illegalTransaction() { //需要抛异常的时候直接抛 if (hasRat()) { logger.error("有内鬼终止交易"); throw new RatException(); } doIllegalTransaction();}(3)controller调用service
@RequestMapping("/test3")public void test3(){ logger.info("test3: RuntimeException"); //Controlelr中不会进行异常处理,也不会手工set错误码,只关心核心操作,其他的统统交给Graceful Response exampleService.illegalTransaction();}(4)在浏览器中请求controller的/test3方法,有异常时将会返回:
{ "status":{ "code":1007, "msg":"有内鬼,终止交易" }, "payload":{ }}案例工程( 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可以非常轻松地解决给这类外部异常定义错误码和提示信息的问题。
以下为操作步骤:
(1)创建异常别名,并用@ExceptionAliasFor注解修饰
@ExceptionAliasFor(code = "1404", msg = "not found", aliasFor = NoHandlerFoundException.class)public class NotFoundException extends RuntimeException {}code为发生NoHandlerFoundException时的错误码,msg为提示信息,aliasFor表示将成为哪个异常的别名。
(2)注册异常别名
创建一个继承了AbstractExceptionAliasRegisterConfig的配置类,在实现的registerAlias方法中进行注册。
@Configurationpublic class GracefulResponseConfig extends AbstractExceptionAliasRegisterConfig { @Override protected void registerAlias(ExceptionAliasRegister aliasRegister) { aliasRegister.doRegisterExceptionAlias(NotFoundException.class); }}(3)浏览器访问不存在的URL
再次访问 http://localhost:9090/example/get2?id=1 ,服务端将返回以下json,正是在ExceptionAliasFor中定义的内容
{ "code":"1404", "msg":"not found", "data":{ }}第一步:引入maven依赖
目前由于作者比较忙,还没有来得及上传至maven中央仓库,可以克隆源码到本地进行编译,然后直接引入到项目中。
源码地址:https://github.com/feiniaojin/graceful-response.git
com.feiniaojin.ddd.ecosystem graceful-response 1.0 第二步:开启Graceful Response
在Spring Boot的配置类上,引入@EnableGracefulResponse注解
@EnableGracefulResponse@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}第三步:开发接口
具体的案例可以参考本项目的示例工程。
案例源码地址:https://github.com/feiniaojin/graceful-response-example.git
特殊情况:
(1)controller方法返回void
例如:
@RequestMapping("/test0")public void test0(){ logger.info("test0: return void");}将会封装为:
{ "status": { "code": 0, "msg": "ok" }, "payload": {}}(2)自定义异常码与错误提示
见上文。
(3)异常别名
见上文。
使用过程中如遇到问题,请私信作者。
| 留言与评论(共有 0 条评论) “” |