Springboot统一信息处理
现在我们的开发模式都是前后端分离的开发方式,前后端的信息交互是我们值得去关注的一个地方。我们采用SpringWeb中的ResponseBodyAdvice接口和@ControllerAdvice、@ExceptionHandler注解完成统一信息处理。
我们先介绍一下ResponseBodyAdvice接口我们看到了它是在控制器方法执行之后编写自定义响应的接口。由(请求映射处理程序适配器)和(异常处理程序异常解析器)匹配我们的自定义响应信息并配合@ControllerAdvice注解完成我们的自定义的统一信息返回.
ResponseBodyAdvice接口中有supports()和beforeBodyWrite()两个方法,这两个方法是定义我们自定义接口响应的方法.
supports():该组件是否支持给定的控制器方法返回类型和选定的HttpMessageConverter类型。
beforeBodyWrite():在选择HttpMessageConverter之后,在调用它的写方法之前调用
下面就看是如何实现的吧!!!
1、统一异常处理
完成统一异常处理我们需要使用到@ControllerAdvice、@ExceptionHandler注解两个注解.
@ControllerAdvice注解是Spring MVC中一个全局的异常处理注解。它可以应用到类上,用来处理控制器中所有带有@RequestMapping注解的方法抛出的异常。这样可以避免在每个控制器中都进行重复的异常处理。
@ExceptionHandler注解是Spring框架中的一个注解,用于处理在Controller中抛出的异常。它可以指定一个方法来处理特定类型的异常,并将异常信息转换为HTTP响应.
1.1定义自定义异常类和异常枚举类
//自定义异常类
public class RestException extends RuntimeException {
private RestResponse<String> restResponse;
public RestException(Integer code, String desc){
super(desc);
this.restResponse = new RestResponse<>();
this.restResponse.setCode(code);
this.restResponse.setDescription(desc);
this.restResponse.setResult(desc);
}
public RestException(RestErrorEnum errorEnum) {
super(errorEnum.getDescription());
this.restResponse = new RestResponse<>();
this.restResponse.setCode(errorEnum.getCode());
this.restResponse.setDescription(errorEnum.getDescription());
}
public RestException(RestErrorEnum errorEnum, String result) {
super(result);
this.restResponse = new RestResponse<>();
this.restResponse.setCode(errorEnum.getCode());
this.restResponse.setResult(result);
this.restResponse.setDescription(errorEnum.getDescription());
}
public Integer getCode() {
return this.restResponse.getCode();
}
public String getResult() {
return this.restResponse.getResult();
}
public String getDescription() {
return this.restResponse.getDescription();
}
}
//自定义枚举类
public enum RestErrorEnum {
SUCCESS(0, "成功"),
//未知错误
UNKNOWN(1001, "未知错误"),
REQUEST_ERROR(1002, "请求错误"),
SYSTEM_ERROR(1003, "系统错误"),
private static Map<Integer, RestErrorEnum> map;
private Integer code;
private String description;
public static RestErrorEnum codeOf(int code) {
if (map == null) {
map = Arrays.stream(RestErrorEnum.values()).collect(Collectors.toMap(RestErrorEnum::getCode, e -> e));
}
return map.getOrDefault(code, UNKNOWN);
}
public static RestErrorEnum descriptionOf(String description) {
for (RestErrorEnum errorEnum : RestErrorEnum.values()) {
if (errorEnum.getDescription().equals(description)) {
return errorEnum;
}
}
return null;
}
RestErrorEnum(Integer code, String description) {
this.code = code;
this.description = description;
}
public Integer getCode() {
return code;
}
public String getDescription() {
return description;
}
}
1.2这里是自定义的实现,如果想更进一步的了解可以点到注解的源码里看一下.完成下面的配置就可以了
@ControllerAdvice
@Slf4j
public class ResponseBodyAdviceEx implements ResponseBodyAdvice<Object> {
@ExceptionHandler(value = RuntimeException.class)
@ResponseBody
public RestResponse<String> errorHandler(Throwable t) {
RestException e;
if (t instanceof RestException) {
e = ((RestException) t);
} else {
t.printStackTrace();
if (t.getCause() != null) {
//减少暴露给用户的异常信息
e = new RestException(RestErrorEnum.UNKNOWN, t.getCause().getMessage());
} else {
e = new RestException(RestErrorEnum.UNKNOWN, t.getMessage());
}
}
return new RestResponse<>(e.getCode(), e.getResult(), e.getMessage());
}
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {//TODO方法实现}
@Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {//方法实现}
}
1.3我们可以使用下面的代码对异常信息抛出给前端.
throw new RestException(RestErrorEnum.PPM_WORKTIME_IS_WORK_CLASS_NAME);
2.统一接口响应
我们上面介绍了ResponseBodyAdvice这个接口的两个方法和基本原理下面看代码!!!
2.1定义一个注解(使用统一配置信息响应就加上的注解)
/**
* 仅在ResponseBodyAdvice 指定的注解下有效。
*/
@Target({TYPE, METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBodyWrapper {
boolean value() default true;
}
2.2定义统一返回的响应类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RestResponse<T> {
private Integer code;
private T result;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String description;
}
2.3自定义方法内的实现
@ControllerAdvice
@Slf4j
public class ResponseBodyAdviceEx implements ResponseBodyAdvice<Object> {
@ExceptionHandler(value = RuntimeException.class)
@ResponseBody
public RestResponse<String> errorHandler(Throwable t) {
RestException e;
if (t instanceof RestException) {
e = ((RestException) t);
} else {
t.printStackTrace();
if (t.getCause() != null) {
//减少暴露给用户的异常信息
e = new RestException(RestErrorEnum.UNKNOWN, t.getCause().getMessage());
} else {
e = new RestException(RestErrorEnum.UNKNOWN, t.getMessage());
}
}
return new RestResponse<>(e.getCode(), e.getResult(), e.getMessage());
}
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
return "org.springframework.http.converter.json.MappingJackson2HttpMessageConverter".equals(converterType.getName());
}
@Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
//response.getHeaders().add("x-frame-options","SAMEORIGIN");
if (body instanceof RestResponse) {
// 对于请求异常情况,设置响应码为500
//response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
return body;
}
if (needToWrapper(returnType)) {
return new RestResponse<>(RestErrorEnum.SUCCESS.getCode(), body, RestErrorEnum.SUCCESS.getDescription());
}
return body;
}
private boolean needToWrapper(MethodParameter returnType) {
// 类含有ResponseBodyWrapper并且value为true
ResponseBodyWrapper type = returnType.getContainingClass().getAnnotation(ResponseBodyWrapper.class);
if (type != null && type.value()) {
return true;
}
// 方法含有ResponseBodyWrapper并且value为true
ResponseBodyWrapper method = returnType.getMethodAnnotation(ResponseBodyWrapper.class);
return method != null && method.value();
}
}
2.4使用时在类或方法上加入自定义的注解即可
@Api(tags = "工时类别")
@RestController
@RequestMapping("worktime/workingclass")
@ResponseBodyWrapper
public class WorkingclassController {}
这里解释一下supports()指定一个HttpmessageConverter的返回类可以参考如下图片
感谢各位读者的阅读,如果持有不同观点或者文章出现错误问题欢迎各位读者评论与私信.如果觉得小编的文章对您有帮助就给小编点个赞吧!!!