背景
spring 提供了 @restcontrolleradvice 用来实现 http 协议的全局异常处理。在异常信息的处理上通常只返回特定的 response 对象,如下。
@slf4j
@restcontrolleradvice
public class restexceptionresolver {
@exceptionhandler(exception.class)
public responseentity<?> processexception(exception ex) {
bodybuilder builder;
response response;
responsestatus responsestatus =
annotationutils.findannotation(ex.getclass(), responsestatus.class);
if (responsestatus != null) {
builder = responseentity.status(responsestatus.value());
response = response.buildfailure("500", responsestatus.reason());
} else {
builder = responseentity.status(httpstatus.internal_server_error);
response = response.buildfailure("500", ex.getmessage());
}
this.process(ex, response);
return builder.body(response);
}
}
作为基础框架,笔者就遇到项目a 要求返回 response1 对象,项目b 要求返回 response2 对象,这个时候,适配起来就很痛苦,例如下方的代码。
@slf4j
@noargsconstructor
@allargsconstructor
@equalsandhashcode(callsuper = false)
@tostring(callsuper = true)
@data
public class response extends dto {
private static final long serialversionuid = 1l;
private boolean success;
private string errcode; // 项目a 要求错误码是字符型
private string errmessage; // 项目a 要求用这个名字
private int code; // 项目b 要求错误码是整型
private string message; // 项目b 要求用这个名字
}
另外,@restcontrolleradvice 只适用于 web 异常捕获,我们还要考虑其他组件的情况,例如 dubbo 捕获 rpc 异常、sentinel 组件触发限流、spring security 安全框架抛出认证异常。
目标
不需要修改基础框架,允许业务方自行扩展异常返回对象。
实现
将 response 提炼为 builder 模式,改为 responsebuilder.builder() 构建返回对象。
@slf4j
@restcontrolleradvice
public class restexceptionhandler {
@exceptionhandler(exception.class)
public responseentity<?> resolveexception(exception ex) {
bodybuilder builder;
object response;
responsestatus status = annotationutils.findannotation(ex.getclass(), responsestatus.class);
if (status != null) {
builder = responseentity.status(status.value());
response = responsebuilder.builder().buildfailure("500", status.reason());
} else {
builder = responseentity.status(httpstatus.internal_server_error);
response = responsebuilder.builder().buildfailure("500", ex.getmessage());
}
this.postprocess(ex);
return builder.body(response);
}
}
public interface responsebuilder<t> {
static responsebuilder<?> builder() {
// 尝试从业务项目获取自定义的 spring bean
responsebuilder<?> builder = applicationcontexthelper.getbean(responsebuilder.class);
if (builder != null) {
return builder;
}
// 如果业务项目没有自定义 bean,返回默认的 builder
return defalutresponsebuilder.getinstance();
}
t buildsuccess();
<body> t buildsuccess(body data);
t buildfailure(string errcode, string errmessage, object... params);
}
public class defalutresponsebuilder implements responsebuilder<response> {
private static final defaultresponsebuilder instance = new defaultresponsebuilder();
private defaultresponsebuilder() {}
public static defaultresponsebuilder getinstance() {
return instance;
}
@override
public response buildsuccess() {
response response = new response();
response.setsuccess(true);
return response;
}
@override
public <body> response buildsuccess(body data) {
singleresponse<body> response = new singleresponse<>();
response.setsuccess(true);
response.setdata(data);
return response;
}
@override
public response buildfailure(string errcode, string errmessage, object... params) {
response response = new response();
response.setsuccess(false);
response.seterrcode(errcode);
response.seterrmessage(messageformatter.arrayformat(message, placeholders).getmessage());
return response;
}
}
@slf4j
@noargsconstructor
@allargsconstructor
@equalsandhashcode(callsuper = false)
@tostring(callsuper = true)
@data
public class response extends dto {
private static final long serialversionuid = 1l;
private boolean success;
private string errcode;
private string errmessage;
}
业务方觉得 response 不能满足需求,重新定义了新对象,如下。
@slf4j
@noargsconstructor
@allargsconstructor
@equalsandhashcode(callsuper = false)
@tostring(callsuper = true)
@data
public class customresponse {
private static final long serialversionuid = 1l;
private boolean success;
private int code; // 要求错误码是整型
private string message; // 前端要求用这个名字
}创建 customresponsebuilder 包装 customresponse 对象,并标记 @component 注解,放入 spring bean 管理。
@component
public class customresponsebuilder implements responsebuilder<customresponse> {
@override
public customresponse buildsuccess() {
customresponse response = new customresponse();
response.setsuccess(true);
return response;
}
@override
public <body> customresponse buildsuccess(body data) {
// 略
}
@override
public customresponse buildfailure(int code, string message, object... params) {
customresponse response = new customresponse();
response.setsuccess(false);
response.setcode(code);
response.setmessage(messageformatter.arrayformat(message, placeholders).getmessage());
return response;
}
}
上述已提到 responsebuilder.builder() 优先查找 spring bean,所以 customresponsebuilder 覆盖了框架内置的 defaultresponsebuilder 类,全局异常捕获器返回结果时,就能返回业务方自定义的 customresponse 对象,这样,不需要改动框架,就能满足业务需求。
产出
根据这个思路,我们分别实现了 web 异常、dubbo 异常、sentinel 限流、security 认证等各种场景的异常处理机制,业务方只需要自行创建 responsebuilder 扩展自己的返回对象即可,不需要修改框架。
到此这篇关于基于spring实现自定义错误信息返回详解的文章就介绍到这了,更多相关spring自定义错误信息内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论