Given the following entities, when I "post" a new entity of "TrainerProfile" and miss some of the @NotNull parameters in "Location", I get a 500 together with a stack trace packed into the JSON instead of a 400 and useful information on what went wrong.
@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;
}
When I post this data to the API
{
...
"location": {
"zipcode": "10000"
}
}
I see the following logs:
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}'}
]
But the REST client sees this:
< 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...
The text is way longer but I spare you the rest.
I configured validation like this:
@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);
}
}
How can I get a proper error message in this case? When I post something that doesn't work for "TrainerProfile" then I get a proper message and error code, but not for the nested object.
Each Spring Data REST request issue a transaction. The outter exception RollbackException
gives you a 500
response, even though this exception actually comes from nested validation failure ConstraintViolationException
. An working but not nice solution should be have a ResponseEntityExceptionHandler
to extract the nested exception, as the auth0 example. The code is here.
Just don't use Spring Data REST where it doesn't fit your requirements.
as said by Spring Data REST leader Oliver Drotbohm in this answer.
A RESTful API should work for aggregate. Aggregate is a DDD concept. Aggregate doesn't work well with relational database. Try No-SQL database such as Mongo DB. The starting point to learn DDD is Oliver's talk.