首页 > 解决方案 > Spring Data Rest:预期 4xx 时出现 500 错误

问题描述

鉴于以下实体,当我“发布”一个“TrainerProfile”的新实体并错过“Location”中的一些@NotNull 参数时,我得到一个 500 以及打包到 JSON 中的堆栈跟踪而不是 400 和有用的信息关于出了什么问题。

@Entity
public class TrainerProfile {
    ...

    @NotNull
    @OneToOne(cascade = CascadeType.ALL)
    private Location location;

}

@Entity
public class Location {

    @Id @GeneratedValue
    private Long id;

    @NotNull
    private String zipcode;

    @NotNull
    private String city;

    @NotNull
    private String country;

}

当我将此数据发布到 API 时

{
  ...
  "location": {
    "zipcode": "10000"
  }
}

我看到以下日志:

javax.validation.ConstraintViolationException: Validation failed for classes [training.edit.provider.model.Location] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
    ConstraintViolationImpl{interpolatedMessage='must not be null', propertyPath=city, rootBeanClass=class training.edit.provider.model.Location, messageTemplate='{javax.validation.constraints.NotNull.message}'}
    ConstraintViolationImpl{interpolatedMessage='must not be null', propertyPath=country, rootBeanClass=class training.edit.provider.model.Location, messageTemplate='{javax.validation.constraints.NotNull.message}'}
]

但是 REST 客户端看到了这一点:

< HTTP/1.1 500
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Access-Control-Allow-Origin: http://localhost:4200
< Access-Control-Allow-Credentials: true
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Tue, 16 Jun 2020 09:33:16 GMT
< Connection: close
<
{"timestamp":"2020-06-16T09:33:16.605+0000","status":500,"error":"Internal Server Error","message":"Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction","trace":"org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction\n\tat org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:543)\n...

文本更长,但我不让你休息。

我这样配置验证:

@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class RestConfiguration implements RepositoryRestConfigurer {

    private final @NonNull Validator validator;

    private final @NonNull UriToIdConverter converter;

    @Override
    public void configureConversionService(ConfigurableConversionService conversionService) {
        RepositoryRestConfigurer.super.configureConversionService(conversionService);
        conversionService.addConverter(converter);
    }

    @Override
    public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
       validatingListener.addValidator("afterCreate", validator);
       validatingListener.addValidator("beforeCreate", validator);
       validatingListener.addValidator("afterSave", validator);
       validatingListener.addValidator("beforeSave", validator);
    }

}

在这种情况下,如何获得正确的错误消息?当我发布不适用于“TrainerProfile”的内容时,我会收到正确的消息和错误代码,但不适用于嵌套对象。

标签: spring-data-rest

解决方案


中间解决方案

每个 Spring Data REST 请求都会发出一个事务。外部异常RollbackException会给你一个500响应,即使这个异常实际上来自嵌套验证失败ConstraintViolationException。一个可行但不是很好的解决方案应该是ResponseEntityExceptionHandler提取嵌套异常,如auth0 示例。代码在这里

建议

只是不要在不符合您要求的地方使用 Spring Data REST。

正如 Spring Data REST 领导者 Oliver Drotbohm 在这个答案中所说。

RESTful API 应该适用于聚合聚合是一个 DDD 概念。聚合不适用于关系数据库。尝试 No-SQL 数据库,例如 Mongo DB。学习 DDD 的起点是Oliver 的演讲


推荐阅读