解决springboot配置@ControllerAdvice不能捕获NoHandlerFoundException问题

解决springboot配置@ControllerAdvice不能捕获NoHandlerFoundException问题

使用springboot开发一个RESTful API服务,配置了@ControllerAdvice,其它类型异常都能正常捕获,就是不能捕获NoHandlerFoundException,安装以往使用springmvc的经验,需要设置DispatcherServlet.throwExceptionIfNoHandlerFound,NoHandlerFoundException就会被DispatcherSevlet抛出,并被@ControllerAdvice捕获处理。想来springboot中自然也是可以的。
网上一搜发现,只需设置spring.mvc.throw-exception-if-no-handler-found=true即可。设置后依然无效!
再次搜索,还需要设置spring.resources.add-mappings=false,问题解决!
很奇怪,为什么禁用了资源映射后,问题就解决了呢?
研究DispatcherServlet源码发现,NoHandlerFoundException异常能否被抛出,关键在如下代码:

mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
	noHandlerFound(processedRequest, response);
	return;
}

只有第一句代码找不到对应该请求的处理器时,才会进入下面的noHandler方法去抛出NoHandlerFoundException异常。
通过测试发现,springboot的WebMvcAutoConfiguration会默认配置如下资源映射:

/映射到/static(或/public、/resources、/META-INF/resources) /webjars/ 映射到classpath:/META-INF/resources/webjars/ /**/favicon.ico映射favicon.ico文件.

这下就明白了,即使你的地址错误,仍然会匹配到/**这个静态资源映射地址,就不会进入noHandlerFound方法,自然不会抛出NoHandlerFoundException了。
所以,我们需要的就是改掉默认的静态资源映射访问路径就可以了。
配置如下属性,NoHandlerFoundException异常就能被@ControllerAdvice捕获了

spring.mvc.throw-exception-if-no-handler-found=true
spring.mvc.static-path-pattern=/statics/**

附上@ControllerAdvice代码:

/**
 * ResponseEntityExceptionHandler预提供了一个处理Spring常见异常的Exceptionhandler
 * @author wangyongjun
 * @date 2018/4/19.
 */
@RestControllerAdvice
public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler {

  /**
   * 覆盖handleExceptionInternal这个汇总处理方法,将响应数据替换为我们的{@link ApiErrorResponse}即可
   * @param ex
   * @param body
   * @param headers
   * @param status
   * @param request
   * @return
   */
  @Override
  protected ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {

    return new ResponseEntity<>(new ApiErrorResponse().setCode(String.valueOf(status.value())).setMsg(ex.getMessage()), status);
  }

  /**
   * 400错误,bad request
   * @param ex
   * @return
   */
  @ExceptionHandler(value = { IllegalArgumentException.class,Exception400.class})
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public ApiErrorResponse badRequestException(Exception ex) {
    return ApiErrorResponse.error400().setMsg(ex.getMessage());
  }

  /**
   * 401错误,未授权
   * @param ex
   * @return
   */
  @ExceptionHandler(value = { Exception401.class})
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public ApiErrorResponse unauthorizedException(Exception ex) {
    return ApiErrorResponse.error401().setMsg(ex.getMessage());
  }

  /**
   * 403错误,权限不足
   * @param ex
   * @return
   */
  @ExceptionHandler(value = { Exception403.class})
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public ApiErrorResponse forbiddenException(Exception ex) {
    return ApiErrorResponse.error403().setMsg(ex.getMessage());
  }

  /**
   * 404错误,处理器不存在异常,资源不存在异常
   * @param ex
   * @return
   */
  @ExceptionHandler(value = { Exception404.class })
  @ResponseStatus(HttpStatus.NOT_FOUND)
  public ApiErrorResponse noHandlerFoundException(Exception ex) {
    return ApiErrorResponse.error404().setMsg(ex.getMessage());
  }

  /**
   * 500异常
   * @param ex
   * @return
   */
  @ExceptionHandler(value = {Exception500.class,Exception.class })
  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  public ApiErrorResponse exception(Exception ex) {

    return ApiErrorResponse.error500().setMsg(ex.getMessage());
  }
  
}

 
   

我使用的是@RestControllerAdvice,异常处理的返回都会被解析为json,不做RESTful API的使用@ControllerAdvice即可。
spring预提供了一个ResponseEntityExceptionHandler,能处理大部分spring自己定义的异常,我们只需要覆盖handleExceptionInternal这个汇总处理方法,将返回的ResponseEntity的body部分替换为我们自己定义的ApiErrorResponse即可。
当然,你也可以自由地配置对其它异常的处理。

转载于:https://my.oschina.net/u/3049656/blog/1798583

你可能感兴趣的:(解决springboot配置@ControllerAdvice不能捕获NoHandlerFoundException问题)