【问题标题】:Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException]为 [class org.springframework.web.bind.MethodArgumentNotValidException] 映射的模棱两可的 @ExceptionHandler 方法
【发布时间】:2016-11-11 22:42:08
【问题描述】:

我正在尝试使用@ControllerAdvice 处理 MethodArgumentNotValidException,代码如下:

@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    private Logger log = LoggerFactory.getLogger(RestResponseEntityExceptionHandler.class);

    @Autowired
    private ApplicationContext applicationContext;

    @ExceptionHandler({ ConstraintViolationException.class })
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorWrapper handleConstraintViolationException(ConstraintViolationException e) {
        String fieldName = e.getConstraintName();
        String message = getResourceMessage(fieldName + ".alreadyExists", "Already Exists");
        return new ErrorWrapper(fieldName + ".error", message);
    }

    @ExceptionHandler({ MethodArgumentNotValidException.class })
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorWrapper handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        return new ErrorWrapper(".error", "test");
    }

    private String getResourceMessage(String key, String defaultMessage) {
        String message = applicationContext.getMessage(key, null, Locale.getDefault());
        if (StringUtils.isNotEmpty(message)) {
            return message;
        }
        return defaultMessage;
    }
}

我正在关注异常

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException]: {public com.ca.bean.ErrorWrapper com.ca.exceptionHandler.RestResponseEntityExceptionHandler.handleMethodArgumentNotValidException(org.springframework.web.bind.MethodArgumentNotValidException), public final org.springframework.http.ResponseEntity org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest)}
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1578) ~[spring-beans-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) ~[spring-beans-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:775) ~[spring-beans-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861) ~[spring-context-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) ~[spring-context-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:444) ~[spring-web-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:326) ~[spring-web-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107) [spring-web-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5099) [catalina.jar:7.0.70]
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5615) [catalina.jar:7.0.70]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147) [catalina.jar:7.0.70]
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1571) [catalina.jar:7.0.70]
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1561) [catalina.jar:7.0.70]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_92]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_92]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_92]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_92]
Caused by: java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException]: {public com.ca.bean.ErrorWrapper com.ca.exceptionHandler.RestResponseEntityExceptionHandler.handleMethodArgumentNotValidException(org.springframework.web.bind.MethodArgumentNotValidException), public final org.springframework.http.ResponseEntity org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest)}
    at org.springframework.web.method.annotation.ExceptionHandlerMethodResolver.addExceptionMapping(ExceptionHandlerMethodResolver.java:109) ~[spring-web-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.web.method.annotation.ExceptionHandlerMethodResolver.<init>(ExceptionHandlerMethodResolver.java:76) ~[spring-web-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.initExceptionHandlerAdviceCache(ExceptionHandlerExceptionResolver.java:265) ~[spring-webmvc-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.afterPropertiesSet(ExceptionHandlerExceptionResolver.java:241) ~[spring-webmvc-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637) ~[spring-beans-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) ~[spring-beans-4.3.0.RELEASE.jar:4.3.0.RELEASE]
    ... 21 common frames omitted

我使用的是 spring 4.3.0。 我想处理 MethodArgumentNotValidException 并想发送自定义消息。我在做什么错误?请帮忙。

【问题讨论】:

标签: spring-4


【解决方案1】:

汉德尔例外

@RestControllerAdvice
@Slf4j
public class ExceptionHandler extends ResponseEntityExceptionHandler {
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        List<String> errorList = ex
                .getBindingResult()
                .getFieldErrors()
                .stream()
                .map(fieldError -> fieldError.getDefaultMessage())
                .collect(Collectors.toList());
        ErrorDetails errorDetails = new ErrorDetails(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errorList);
        return handleExceptionInternal(ex, errorDetails, headers, errorDetails.getStatus(), request);
    }
}

自定义错误信息

@Data
public class ErrorDetails {
    private HttpStatus status;
    private String message;
    private List<String> errors;

    public ErrorDetails(HttpStatus status, String message, List<String> errors) {
        super();
        this.status = status;
        this.message = message;
        this.errors = errors;
    }

    public ErrorDetails(HttpStatus status, String message, String error) {
        super();
        this.status = status;
        this.message = message;
        errors = Arrays.asList(error);
    }
}

示例响应

  {
      "status": "BAD_REQUEST",
      "message": "Validation failed for argument [0] in public void com.ns.hospitalmanagement.resource.PatientResource.createPatient(com.ns.hospitalmanagement.entity.Patient) with 2 errors: [Field error in object 'patient' on field 'name': rejected value [null]; codes [NotNull.patient.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [patient.name,name]; arguments []; default message [name]]; default message [Name Can not be Null]] [Field error in object 'patient' on field 'name': rejected value [null]; codes [NotEmpty.patient.name,NotEmpty.name,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [patient.name,name]; arguments []; default message [name]]; default message [Name Can not be Empty]] ",
      "errors": [
        "Name Can not be Null",
        "Name Can not be Empty"
      ]
    }

【讨论】:

    【解决方案2】:

    尝试重写方法handleMethodArgumentNotValid, 在我的情况下,方法变成这样:

      @Override
      protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
                                                                    HttpHeaders headers, HttpStatus status,
                                                                    WebRequest request) {
        String errorMessage = ex.getBindingResult().getFieldErrors().stream()
                     .map(DefaultMessageSourceResolvable::getDefaultMessage)
                     .findFirst()
                     .orElse(ex.getMessage());
        return response(ex, request, HttpStatus.BAD_REQUEST, errorMessage);
      }
    
      private ResponseEntity<Object> response(Exception ex, WebRequest request, HttpStatus status,
                                              String message) {
        return handleExceptionInternal(ex, message, header(), status, request);
      }
    

    【讨论】:

      【解决方案3】:

      Springs ResponseEntityExceptionHandler 有一个方法handleException,它带有注释:

      @ExceptionHandler({     
              ...
              MethodArgumentNotValidException.class,
              ... 
          })
      

      您的方法 handleMethodArgumentNotValidException 也被注释为处理 MethodArgumentNotValidException。所以spring找了两个方法应该用来处理同一个异常,这就是异常的原因。

      **解决方案** 不要添加新方法 handleMethodArgumentNotValidException 而只是覆盖方法 ResponseEntityExceptionHandler.handleMethodArgumentNotValid ,并且不要对其进行注释。 您的类 ErrorWrapper 必须为此扩展 ResponseEntity。

      【讨论】:

      • handleException 方法是最终方法。因此我们不能覆盖它。但是该方法对 MethodArgumentNotValidException 进行 instanceof 检查并调用名称为 handleMethodArgumentNotValid 的方法。所以我尝试覆盖 handleMethodArgumentNotValid 方法并且它有效。非常感谢。
      • @Krishna 有没有可能展示你是如何解决这个问题的?
      • @BigDong...我需要查看我现在没有的代码。生病检查并提供代码,但需要 9-10 小时
      • @BigDong..在这种情况下,您可以捕获父异常并在内部进行检查并继续
      • 然后呢?有@Order(9000) 解决方案吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-08
      • 2020-05-18
      相关资源
      最近更新 更多