记录一次全局异常告警@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,我得到的应该是这样的:

 

 

 

为什么呢? 在我的全局异常处打印断点,发现确实是走到了断点处。

  

 继续断点:发现在选择选择器时候仍然是对的:

 

posted @ 2023-05-17 15:23  Doyourself!  阅读(342)  评论(0编辑  收藏  举报