Problem
Our API uses a snake_case naming strategy for DTO deserialization and serialization. However, when validation fails, the error messages still use the original object property names, ignoring the configured naming strategy, which leads to inconsistent API responses.
Solution
This issue occurs because bean validation happens after the incoming request has been deserialized into the DTO, but before the response is serialized. As a result, validation errors report the Java field names rather than the JSON property names defined by the configured naming strategy.
First, make sure that the property naming strategy is configured as shown below:
# application.properties
spring.jackson.property-naming-strategy=SNAKE_CASE
Override the default error generation with creating a custom error handler.
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
// TODO: To be implemented
}
}
To access the mapped field names, inject an ObjectMapper and resolve the JSON property names by iterating over the list of field errors.
private final ObjectMapper objectMapper;
//...
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
List<String> errors = exception.getBindingResult()
.getFieldErrors()
.stream()
.map(this::formatErrorMessage)
.toList();
return ResponseEntity.badRequest().body(Map.of("errors", errors));
}
private String formatErrorMessage(FieldError fieldError) {
PropertyNamingStrategy mapping = objectMapper.getPropertyNamingStrategy();
String mappedFieldName = mapping.nameForField(null, null, fieldError.getField());
return String.format("%s.%s: %s", fieldError.getObjectName(), mappedFieldName, fieldError.getDefaultMessage());
}
Let’s test it!
$ curl -XPOST "http://api.yourdomain.com/v1/merchants" \
-H "Content-Type: application/json" \
-d "{}"
{
"errors" : [
"merchantCreateRequest.partner_id: must not be null",
"merchantCreateRequest.merchant_name: must not be null"
]
}%
You can find all code examples in the following repository:
https://github.com/ibrahimgunduz34/blog-code-examples/tree/master/spring-boot-validation-error
Thanks for reading!
Top comments (0)