Spring Boot - ControllerAdvice exceptionhandler没有使用put和patch请求触发

时间:2014-07-07 17:51:18

标签: spring validation rest spring-boot

使用最新版本的SpringBoot(1.1.3.RELEASE),我正在尝试一个非常简单的休息应用程序。我有一个异常处理程序来处理由输入错误引起的验证错误。如果我发出POST请求,则会触发异常处理程序。但是,如果我有补丁或放置请求,则不会触发。可能会发生什么?

异常处理程序类

@ControllerAdvice
public class DataValidationExceptionHandler {
    @ExceptionHandler(value = {ConstraintViolationException.class})
    public ResponseEntity handleBadInput(ConstraintViolationException ex) {
        return new ResponseEntity(HttpStatus.BAD_REQUEST);
    }
}

主要应用类

@Configuration
@ComponentScan
@EnableJpaRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableTransactionManagement
@EnableAutoConfiguration
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

域类

@Entity
public class Company {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    @Size(min=2, max=100)
    @NotBlank
    private String name;
    @URL
    private String website;

   //Not including getters and setters...
}

存储库类

@RepositoryRestResource(collectionResourceRel = "companies", path = "companies")
public interface CompanyRepository extends PagingAndSortingRepository<Company, Long> {
}

正确处理此POST请求。该网站应该是一个URL,在此请求中它不是,因此,它正确地生成验证错误并由异常处理程序处理:curl -H "Content-Type:application/json" -X POST -i -d '{"name":"comp1", "website":"www.comp1.com"}' http://localhost:8080/companies

此PUT请求不由异常处理程序处理,尽管它生成与post请求相同的constraintviolationexception - curl -H "Content-Type:application/json" -X PUT -i -d '{"name":"comp1", "website":"www.comp1.com"}' http://localhost:8080/companies/1

发生了什么事?


更新。在POST请求的情况下,由于ExceptionHandler正在接管,因此没有生成堆栈跟踪。在PUT请求的情况下,这里是堆栈跟踪转储

javax.validation.ConstraintViolationException: Validation failed for classes [com.mycompany.domain.Company] during update time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
    ConstraintViolationImpl{interpolatedMessage='must be a valid URL', propertyPath=website, rootBeanClass=class com.mycompany.domain.Company, messageTemplate='{org.hibernate.validator.constraints.URL.message}'}
]
    at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:160)
    at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreUpdate(BeanValidationEventListener.java:103)
    at org.hibernate.action.internal.EntityUpdateAction.preUpdate(EntityUpdateAction.java:257)
    at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:134)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:349)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:515)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272)

更新2 - 要进行故障排除,我还添加了一个catch-all异常处理程序。对于PUT请求,此catch-all将处理异常而不是我期望的异常(javax.validation.ConstraintViolationException的处理程序)。在查看异常的调试器时,调试器会显示 - org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction

@ControllerAdvice
public class SystemExceptionHandler {
    @ExceptionHandler(value = {Exception.class})
    public ResponseEntity handleBadInput(Exception ex) {
        return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

所以看起来Spring将javax.validation.ConstraintViolationException的异常包装到另一个异常中。要查看是否正确处理了包装的异常,我还添加了此处理程序

@ControllerAdvice
public class TransactionExceptionHandler {
    @ExceptionHandler(value = {TransactionSystemException.class})
    public ResponseEntity handleTxException(TransactionSystemException ex) {
        Throwable t = ex.getCause();
        if(t instanceof ConstraintViolationException){
           return new ResponseEntity(HttpStatus.BAD_REQUEST);    
        }else {
            return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

问题是即使是TransactionSystemException.class的处理程序也没有处理异常。它只有Exception.class才能处理它。神秘。


更新3 - 我现在在我的智慧结束。异常处理程序没有被正确调用。我的下一个测试是查看是否正确处理了数据库约束违规。在非常简单的公司模型中,数据库的设置对公司名称有唯一约束。因此,如果发生违规,则引发的异常是org.springframework.dao.DataIntegrityViolationException。无法捕获所有Exception.class处理程序以及为org.springframework.dao.DataIntegrityViolationException编写的特定处理程序。 Boot的json输出是这样的。 WHAT ???

{
  "cause" : {
    "cause" : {
      "cause" : {
        "cause" : null,
        "message" : "Duplicate entry 'comp1' for key 'chk_comp_name_unique'"
      },
      "message" : "Duplicate entry 'comp1' for key 'chk_comp_name_unique'"
    },
    "message" : "could not execute statement"
  },
  "message" : "could not execute statement; SQL [n/a]; constraint [chk_comp_name_unique]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement"
}

1 个答案:

答案 0 :(得分:0)

看起来这将在下一版本的Spring Data Rest中修复。 jira-ticket

我在github repo中看到了代码更改,因此SDR 2.3 RC1(Fowler)可以使用该修复程序。