Closed humbertoc-silva closed 3 days ago
I've only looked at the first branch, and it's hard to know exactly what you're looking at as the MethodArgumentNotValidException
has quite a bit of state, but there seems to be a misunderstanding about the default message.
The default message in the object errors is resolved by looking up jakarta.validation.constraints.NotBlank.message
or jakarta.validation.constraints.Size.message
. If you add one or both of these to messages.properties
you should see that the default message in the error changes accordingly.
I'm not going to investigate further at this point as I suspect the second and third problems may be a knock-on effect of the misunderstanding that's caused the first. If applying the change suggested above does not help with the second and third problems and you would like us to investigate further, please update them so that there's a test that we can run that precisely reproduces the problem rather than us trying to guess what part of the state in the debugger it is that you consider to be incorrect.
As I understood you want to interpolate and include validation errors in the detail
field.
I checked your first branch spring-doc
and to achieve this you need to adjust a little bit your messages.properties
messages.properties
NotBlank.person.name=The field {0} must not be blank
Size.person.name=The size of the {0} field must be between {2} and {1}
problemDetail.org.springframework.web.bind.MethodArgumentNotValidException={0}{1}
If you would like to support pt_BR
locale you also have to add the following file:
messages_pt_BR.properties
NotBlank.person.name=O campo {0} n\u00e3o deve estar em branco
Size.person.name=O tamanho do campo {0} deve estar entre {2} e {1}
HTTP Request:
POST http://localhost:8080/people
Content-Type: application/json
Accept-Language: pt-BR
{
"name": ""
}
HTTP Response:
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "O campo name não deve estar em branco, and O tamanho do campo name deve estar entre 1 e 50",
"instance": "/people"
}
I've only looked at the first branch, and it's hard to know exactly what you're looking at as the
MethodArgumentNotValidException
has quite a bit of state, but there seems to be a misunderstanding about the default message.The default message in the object errors is resolved by looking up
jakarta.validation.constraints.NotBlank.message
orjakarta.validation.constraints.Size.message
. If you add one or both of these tomessages.properties
you should see that the default message in the error changes accordingly.I'm not going to investigate further at this point as I suspect the second and third problems may be a knock-on effect of the misunderstanding that's caused the first. If applying the change suggested above does not help with the second and third problems and you would like us to investigate further, please update them so that there's a test that we can run that precisely reproduces the problem rather than us trying to guess what part of the state in the debugger it is that you consider to be incorrect.
Hi @wilkinsona, thank you for the reply. The main problem that I tried to show in the spring-doc branch was that maybe the Spring documentation was incomplete. I know that Bean Validation has this default message code and if I put them in my messages.properties the message will work. But I am trying to do the things as the Spring documentation explains, using the documentation example:
record Person(@Size(min = 1, max = 10) String name) {
}
@Validated
public class MyService {
void addStudent(@Valid Person person, @Max(2) int degrees) {
// ...
}
}
The example does not use the message property and I tried to do that same way, so I got the default Bean Validation message.
On the branch spring-doc-with-message I put the Spring code on the message attribute, I got the message but Spring did not interpolate the messages.
And on the bean-validation it was worst, using Bean Validation interpolation way Spring Boot broke with an exception.
As I understood you want to interpolate and include validation errors in the
detail
field.I checked your first branch
spring-doc
and to achieve this you need to adjust a little bit yourmessages.properties
messages.properties
NotBlank.person.name=The field {0} must not be blank Size.person.name=The size of the {0} field must be between {2} and {1} problemDetail.org.springframework.web.bind.MethodArgumentNotValidException={0}{1}
If you would like to support
pt_BR
locale you also have to add the following file:messages_pt_BR.properties
NotBlank.person.name=O campo {0} n\u00e3o deve estar em branco Size.person.name=O tamanho do campo {0} deve estar entre {2} e {1}
HTTP Request:
POST http://localhost:8080/people Content-Type: application/json Accept-Language: pt-BR { "name": "" }
HTTP Response:
{ "type": "about:blank", "title": "Bad Request", "status": 400, "detail": "O campo name não deve estar em branco, and O tamanho do campo name deve estar entre 1 e 50", "instance": "/people" }
Hi @nosan, thank you for the reply.
Yes, if I try to use the detail message it will work, but this occurs because Spring finishes the interpolation after validation using the method org.springframework.web.ErrorResponse#updateAndGetBody
, but if you see the individual field messages they will be incomplete, without interpolation and this is the problem that I showed on the second branch, spring-doc-with-message.
I will update the branch spring-doc-with-message to return the messages without interpolation, this way will be easier to see my point.
I need to customize individual validation messages with Spring way (using placeholders like {0}...) or Bean Validation way (using expressions and parameters values like {min}, {max}).
I have just updated the branch spring-doc-with-message, now it is possible to see that the validation messages were not interpolated appropriately.
Result:
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Invalid request content.",
"instance": "/people",
"errors": {
"Size": "The size of the {0} field must be between {1} and {2}",
"NotBlank": "The field {0} must not be blank"
}
}
Some time ago, Spring Boot introduced Bean Validation Message Interpolation via MessageSource
(see: PR #17530).
The primary goal of this enhancement was to utilize MessageSource
to replace any placeholders, and then, delegate the final interpolation to Hibernate's Bean Validation.
Let’s consider the following example:
message.properties
NotBlank.person.name=The field name must not be blank
Size.person.name=The size of the name field must be between {min} and {max}
Additionally, if you remove the ExceptionHandlerController
and add server.error.include-binding-errors=always
to your application.properties
file, and then make an HTTP request, you will get the following result:
{
"timestamp": "2024-10-17T20:13:29.504+00:00",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"Size.person.name",
"Size.name",
"Size.java.lang.String",
"Size"
],
"arguments": [
{
"codes": [
"person.name",
"name"
],
"arguments": null,
"defaultMessage": "name",
"code": "name"
},
50,
1
],
"defaultMessage": "The size of the name field must be between 1 and 50",
"objectName": "person",
"field": "name",
"rejectedValue": "",
"bindingFailure": false,
"code": "Size"
},
{
"codes": [
"NotBlank.person.name",
"NotBlank.name",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
{
"codes": [
"person.name",
"name"
],
"arguments": null,
"defaultMessage": "name",
"code": "name"
}
],
"defaultMessage": "The field name must not be blank",
"objectName": "person",
"field": "name",
"rejectedValue": "",
"bindingFailure": false,
"code": "NotBlank"
}
],
"path": "/people"
}
As you can see, the interpolation works as expected.
However, when you have added ExceptionHandlerController
extending ResponseEntityExceptionHandler
, things changed significantly.
The main issue is that the Spring Framework also attempts to resolve Bean Validation's codes using MessageSource
. For the Person.name field that is being validated, it will attempt to resolve the following codes:
[Size.person.name, Size.name, Size.java.lang.String, Size]
[person.name, name]
[NotBlank.person.name, NotBlank.name, NotBlank.java.lang.String, NotBlank]
As you can see, the code NotBlank.person.name
is present in message.properties with {min} and {max} placeholders. Since the Spring Framework does not know what {min} and {max} represent, this leads to the following exception:
Failure in @ExceptionHandler com.example.demo.ExceptionHandlerController#handleException(Exception, WebRequest)
java.lang.IllegalArgumentException: can't parse argument number: min
With that in mind, I can suggest the following options:
message.properties
which do not overlap with Spring Framework. For example NotBlankPersonName
. You will be able to use {min}, {max}, ${validatedValue}, etc. and will have fully interpolated message provided by Spring Boot and Hibernate. ResponseEntityExceptionHandler
. Same as first option, but don't need to think about code overlaps. ValidationMessages.properties
instead of message.properties
?Thanks @nosan! It doesn't look like this is a Spring Boot bug so I'll close the issue.
@nosan and @wilkinsona I took some time to investigate how things work deeply and understood exactly how Spring works with Bean Validation. It was a misunderstanding on my side believing that Spring would interpolate the messages automatically. I saw that I needed to use one of the MessageSource#getMessage
on my own to get the interpolated message. Now I can choose between Bean Validation way or Spring way interpolation without any errors, and if I decide to go with Spring way I know that I need to use some getMessage
method.
Thank you.
Hi,
I am using:
I am using the default Spring Boot auto-configuration, there is no customization on the project. I have a Controller Advice that is inherent from
org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
, and I want to use Problem Details as a response error format in my project.1 - I need to customize my validation messages and interpolate some values. I am trying to follow the Spring Framework documentation. However following the documentation instructions I got the default messages from Bean Validation (in my language pt-BR).
2 - Then I tried to put the Spring codes on the annotations message attribute. I got the messages from the
messages.properties
file but the arguments {0}, {1}, {2}, etc. do not were interpolated by Spring.3 - Finally, I changed the strategy and resolved to use the Bean Validation interpolation format, I got the correct messages, but when Spring Boot tried to resolve the Problem Detail fields I got an unexpected exception.
The following project can be used to simulate the problems: spring-boot-bean-validation-message-interpolation-issue Public
Three branches simulate the respective problems:
1 - spring-doc 2 - spring-doc-with-message 3 - bean-validation
I read the Spring documentation many times and debugged the project, but I did not find a way to make the project work as expected.
Let me know if I missed some steps to make Bean Validation work with Spring Boot and be able to customize my messages according to the official documentation.