全局统一结构处理
全局统一处理分为异常的处理和结果的统一包装,filter 过滤器用于在 filter 在执行时出现的错误然后进行统一的包装结果;Advice 切面增强则是在接口调用时对返回值或者异常进行处理;
如果集成了swagger,那么需要在Filter中释放掉
path:
ignores: #忽略的请求路径
- "/swagger-ui.html/**"
- "/v2/**"
- "/swagger-resources/**"
- "/webjars/**"
@AllArgsConstructor
public class UnifiedResultFilter extends OncePerRequestFilter {
/** 将需要忽略的路径配置到yml文件中通过自动注入的方式进行获取 */
private final ApplicationProperties applicationProperties;
/** ant风格表达式匹配器 */
private final AntPathMatcher matcher = new AntPathMatcher();
/**
* Do filter internal
*
* @param httpServletRequest http servlet request
* @param httpServletResponse http servlet response
* @param filterChain filter chain
* @throws ServletException servlet exception
* @throws IOException io exception
* @since 1.0.0
*/
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
FilterChain filterChain) throws ServletException, IOException {
ContentCachingResponseWrapper contentCachingResponseWrapper = new ContentCachingResponseWrapper(httpServletResponse);
try {
filterChain.doFilter(httpServletRequest, contentCachingResponseWrapper);
} catch (Exception e) {
wrapResponse(contentCachingResponseWrapper, e);
}
//刷新缓存数据,写入到真实的response中
contentCachingResponseWrapper.copyBodyToResponse();
}
/**
* Wrap response
*
* @param contentCachingResponseWrapper content caching response wrapper
* @throws IOException io exception
* @since 1.0.0
*/
private void wrapResponse(ContentCachingResponseWrapper contentCachingResponseWrapper,
Exception e) throws IOException {
byte[] resultBytes = contentCachingResponseWrapper.getContentAsByteArray();
if (resultBytes.length > 0) {
//重置一下读取的指针
contentCachingResponseWrapper.resetBuffer();
}
ServletOutputStream outputStream = contentCachingResponseWrapper.getOutputStream();
outputStream.write(JacksonUtils.toJsonAsBytes(Result.failed(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value())));
}
/**
* 不需要进行拦截的路径,默认放掉swagger相关
*
* @param request request
* @return boolean boolean
* @since 1.0.0
*/
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("[/]+$", "");
List<String> ignores = applicationProperties.getPath().getIgnores();
//ant风格表达式进行匹配请求路径
return ignores.stream().anyMatch(p -> matcher.match(p, path)) || !StringUtils.hasText(path);
}
}
通过配置类注入进去,可以通过 FilterRegistrationBean 来执行注入Filter
@Configuration
public class ApplicationAutoConfig {
/**
* 添加跨域
*
* @return the filter registration bean
* @since 1.0.0
*/
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("http://localhost:8080");
//是否发送 Cookie
config.setAllowCredentials(true);
//放行哪些请求方式
config.addAllowedMethod("*");
//放行哪些原始请求头部信息
config.addAllowedHeader("*");
//暴露哪些头部信息
config.addExposedHeader("*");
//2. 添加映射路径
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**", config);
//3. 返回新的CorsFilter
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean(new CorsFilter(corsConfigurationSource));
bean.setOrder(0);
bean.addUrlPatterns("/*");
return bean;
}
@Bean
public FilterRegistrationBean<ResponseFilter> responseFilter() {
FilterRegistrationBean<ResponseFilter> bean = new FilterRegistrationBean(new ResponseFilter());
bean.setOrder(Integer.MAX_VALUE);
return bean;
}
}
接口异常处理
@RestControllerAdvice
public class GlobalException {
//可以自定义异常信息处理
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseData<String> handleBusinessLogicException(Exception e) {
return ResponseData.failure(e.getMessage());
}
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseData<String> handlerSystemException(Throwable e) {
return ResponseData.failure(e.getMessage());
}
}
接口结果处理
@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ResultAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
ResponseData<Object> result = null;
if (o instanceof Result) {
result = (ResponseData<Object>) o;
} else {
result = ResponseData.ok(o);
}
return result;
}
}
数据格式
@Data
public class ResponseData<T> {
private T data;
private Integer status;
private String msg;
public static <T> ResponseData<T> ok(T result) {
return ok(result, "success");
}
public static <T> ResponseData<T> ok(T result, String msg) {
ResponseData<T> responseData = new ResponseData<>();
responseData.setData(result);
responseData.setStatus(HttpStatus.OK.value());
responseData.setMsg(msg);
return responseData;
}
public static <T> ResponseData<T> failure(String msg) {
ResponseData<T> responseData = new ResponseData<>();
responseData.setMsg(msg);
responseData.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
return responseData;
}
}