记录一次全局异常告警@ExceptionHandler和HandlerExceptionResolver的问题
最近有同事说之前写的全局异常告警,如果有@Valid的注解,在接入新写的插件告警后,返回信息不打印了。全局异常是基于@ExceptionHandler的全局异常类,主要是Servlet MVC的ModelAndView返回的错误信息的捕获。代码如下:
/** * @author xxx */ @RestControllerAdvice public class DefaultGlobalExceptionHandler { private static final Logger log = LoggerFactory.getLogger(DefaultGlobalExceptionHandler.class); /** * @param ex * @return 响应对象 * @description 基础异常 */ @ExceptionHandler(value = {BaseException.class}) public Result baseException(BaseException ex) { log.error("baseException:{}", ex); return Result.fail(ex.getErrorCode(), ex.getErrorMsg()); } @ExceptionHandler(value = {Exception.class}) public Result exception(Exception ex) { log.error("Exception:{}", ex); return Result.fail(); } @ExceptionHandler(value = {MaxUploadSizeExceededException.class}) public Result exception(MaxUploadSizeExceededException ex) { log.error("RuntimeException:{}", ex); return Result.error(CommonResultStatus.COMMON_ERROR_UPLOAD_SIZE_EXCEED.getCode(),"上传文件大小超出限制!"); } @ExceptionHandler(value = {RuntimeException.class}) public Result exception(RuntimeException ex) { log.error("RuntimeException:{}", ex); return Result.fail(); } /** * 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常 * * @param ex * @return com.gwm.marketing.common.result.Result * @author xxx * @date 15:01 2021/6/11 */ @ExceptionHandler(BindException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result validExceptionHandler(BindException ex) { String message = ex.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(",")); return Result.error(CommonResultStatus.COMMON_INVALID_PARAM.getCode(),CommonResultStatus.COMMON_INVALID_PARAM.getMessage()+":["+message+"]"); } /** * 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常 * * @param ex * @return * @author * @date 15:15 2021/6/11 */ @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result ConstraintViolationExceptionHandler(ConstraintViolationException ex) { String message = ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(",")); return Result.error(CommonResultStatus.COMMON_INVALID_PARAM.getCode(),CommonResultStatus.COMMON_INVALID_PARAM.getMessage()+":["+message+"]"); } /** * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常 * * @param ex * @return com.gwm.marketing.common.result.Result * @author xxx * @date 15:16 2021/6/11 */ @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) { String message = ex.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(",")); return Result.error(CommonResultStatus.COMMON_INVALID_PARAM.getCode(),CommonResultStatus.COMMON_INVALID_PARAM.getMessage()+":["+message+"]"); } @ExceptionHandler(value = {Throwable.class}) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public Result throable() { return Result.fail(); } }
加入的插件告警是基于@WebMvcConfigurer,里面重写了对于异常的处理,重写了HandlerExceptionResolver,如果有异常信息,通过钉钉告警的方式实时通知。加了这么一个功能。同事给我说了之后,我本地使用postman发现本地是能复现的,于是开始一点点的调试追踪。
首先是重写WebMvcConfigurer
package com.gwm.marketing.filter; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.gwm.marketing.filter.exception.DingdingHandleException; import com.gwm.marketing.filter.intercepter.*; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; import org.jetbrains.annotations.NotNull; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.*; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.config.annotation.*; import java.io.IOException; import java.util.List; import java.util.Optional; /** * @Author fanht * @Description 拦截器参数校验 * @Date 2021/11/12 2:05 下午 * @Version 1.0 */ @Configuration public class OraDingdingConfigurer implements WebMvcConfigurer, Interceptor { /** * 拦截器参数校验 * * @param interceptorRegistry */ @Override public void addInterceptors(InterceptorRegistry interceptorRegistry) { //注意拦截器的顺序 interceptorRegistry.addInterceptor(new CharacterValidInterceptors()); interceptorRegistry.addInterceptor(new HttpParamVerifyInterceptor()); interceptorRegistry.addInterceptor(new HttpRequestStatisticsInterceptor()); } @Override public void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) { } @Override public void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) { } @Override public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) { } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) { } @Override public void addFormatters(FormatterRegistry formatterRegistry) { } @Override public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) { } @Override public void addCorsMappings(CorsRegistry corsRegistry) { } @Override public void addViewControllers(ViewControllerRegistry viewControllerRegistry) { } @Override public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) { } @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) { } @Override public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) { } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> list) { } @Override public void extendMessageConverters(List<HttpMessageConverter<?>> list) { //新版本中的 Optional<HttpMessageConverter<?>> optional = list.stream() .filter(o -> o instanceof MappingJackson2HttpMessageConverter) .findFirst(); if (optional.isPresent()) { MappingJackson2HttpMessageConverter converter = (MappingJackson2HttpMessageConverter) optional.get(); ObjectMapper mapper = converter.getObjectMapper(); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } } @Override public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) { } @Override public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) { //todo 添加全局异常,因为HandlerInterceptor不能显示异常 之前是写到了configureHandlerExceptionResolvers 里面,使用哪个会和全局异常DefaultGlobalExceptionHandler有先后顺序问题 list.add(0,new DingdingHandleException()); } @Override public Validator getValidator() { return null; } @Override public MessageCodesResolver getMessageCodesResolver() { return null; } @NotNull @Override public Response intercept(@NotNull Chain chain) throws IOException { Request request = chain.request(); return chain.proceed(request); } @Bean public DispatcherServlet dispatcherServlet(){ return new OraDispatcherServlet(); } }
主要是里面的configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list)
原来我是直接实现configureHandlerExceptionResolvers,在
@Override public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) { list.add(new DingdingHandleException()); }
DingdingHandleException()代码如下
Controller类:
看到前面有一个@Valid的注解,AuthModelParam的实体类:
但是每次调试发现,哪个全局异常确实是走到了,但是在走到下一步的ModelAndView的解析器时候,发现是有问题的。使用postman看到的结果是这样的:
实际上,如果没有这个自定义的DingdingHandleException,我得到的应该是这样的:
为什么呢? 在我的全局异常处打印断点,发现确实是走到了断点处。
继续断点:发现在选择选择器时候仍然是对的:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
2022-05-17 @Transaction 更新后异步再去获取主键id失效
2018-05-17 代理能访问外网却不能访问内网记录
2017-05-17 idea 使用 git打成jar包到 nexus