-
Notifications
You must be signed in to change notification settings - Fork 38.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve the experience of handling validation errors in controller methods #26219
Comments
Also worth mentioning. Actually, it would be great (although not sure if possible) if |
I found one more thing, that during handling of So, the point of this issue to simply highlight that this needs to be somehow redesigned because these things are very tightly coupled although I don't have a direct implementation suggestion in mind. |
And one more: |
And one more: |
Also For example, if I search for |
+1 for this, purely because it will provide a way to ensure that the Error code API can be used consistently for IoC JSR 380 validation failures. Worth noting that the ConstraintViolationException appears to default to being an Internal Server Error as of v2.4.0. Excuse this if this appears completely out of place for me to suggest this, as my intention is definitely not to cause insult! Currently, with the state of the framework sometimes triggering ConstraintViolation, and sometimes MethodArgumentNotValidException, I cannot make use of the error codes that are provided. In this sense, it kind of makes using the binding error response shape pointless, as one cannot maintain a consistent response shape. For this reason, I currently have to work around these issues and roll my own set of validation, attempting to unify these cases into something that when put down on a swagger spec, appears to be consistent. This would be a massive help towards being able to write consistent and reusable components and validation rules to use across a platform of microservices that use Spring MVC and Spring WebFlux :) As a slightly off-topic on-topic suggestion...As a slightly off-topic on-topic suggestion, it would be nice to eventually have a way to traverse the PropertyPath of the internal constraint violation that was triggered by the JSR-380 implementation, but in such a way that we can reflectively inspect each element that we are attempting to look at (i.e. get a Class or Parameter directly for each nested part of the validation. The reason for suggesting this, is that we could then essentially have logic that identifies all the annotations on an annotated element that fails validation (currently, we can only view the annotation that caused the constraint violation). By allowing this, we would have the ability to produce responses such as the following: {
"status": 400,
"error": "Bad Request",
"message": "One or more validation failures occurred in the request",
"invalidParameters": {
"headers": [{
// How can I consistently get this header name in this format for my response messages?
// the constraint violation format makes this overly difficult. If I could reflect to see if there
// was a @RequestHeader annotation, this would be much easier.
"Correlation-ID": "must be specified"
}],
"body": [{
// we currently have to do all the reflection manually that has to discover the json naming
// conventions in use, taking into acount @JsonUnwrapped, @JsonProperty. If we have
// to provide XML, we need the ability to consider JAXB too, etc. This means if our API
// has to use kebab-case naming, we can still provide sensible meaningful and potentially
// machine-readable responses to the consumer if we need to, without mixing up conventions
// that may reduce the meaningfulness of the validation violation location.
"location": "$.user.roles[2].name",
"reason": "must not be blank"
}]
}
} Making reference to one of the JSON path guidelines that can be found online. Whether this would come under the scope of being implementation detail on behalf of JSR-303 and JSR-370 or not, I would not like to say, but the reason I bring this up is that it may be overly beneficial to take a larger look at how this API is presented to those who may be using Spring Web MVC or WebFlux to produce integration-level microservices rather than full-stack applications that also serve their front end. While I appreciate that Spring has opinionated defaults, currently it can be very difficult to migrate to Spring as your primary framework for web development when you have a set of business defined guidelines respecting the shape of response messages you provide, as it can result in writing a lot of boilerplate code to work around the current limitations of how the design is presented. I guess as a TLDR, I am wondering whether it is worth considering how the validation API is presented overall in terms of usage for Web validation, as currently it can cause a lot of work if you are unable to follow the exact designs that Spring has to provide. Ideally we would like to be able to use Spring alongside our existing standards for how to provide responses. If this is completely out of place to suggest this, then that is fine, and I apologise for placing this wall of text here, but my hope is that if not, there may be other suggestions on how to work around the issues in the original message among others with providing specific representations of errors in a way that allows migration from other frameworks without having to rewrite all consumers of a service to work with the new validation model. |
@ascopes thanks for your input! I kind of agree that About About property path, not sure what you mean. You can access About correlation id, not sure again why would you want it inside the error response body, distributed tracing is usually done in headers, not in the body. You would still have the trace id in the response header even if that was a successful response. I'm not sure if I missed something or not, let me know. |
What you have suggested sounds like a good plan; I am happy with that. The main thing about the ability to access other annotations is not necessarily to get other validation info, as much as it is an ability to introspect annotations for say, JAXB or Jackson or whatever is in use. This would provide the ability to give accurate XPath or JSONPath references to the erroneous element that failed validation. While we can use the property path to some extent, this kind of begins to break down when using annotations such as <!-- Based off of RFC-7807's example format, modified for this example -->
<problem>
<status>400</status>
<title>Bad Request</title>
<detail>Validation failures occurred</detail>
<invalidParams>
<invalidParam>
<in>headers</in>
<name>X-RateLimitUnit</name>
<detail>Must be either seconds or milliseconds</detail>
</invalidParam>
<invalidParam>
<in>body</in>
<location>//user/authorities[@type='role'][5]</location>
<type>role</type>
<detail>Role names must not contain spaces</detail>
</invalidParam>
</invalidParams>
</problem> which would for a corresponding JSON request for the same schema, perhaps look like this: {
"status": 400,
"title": "Bad Request",
"detail": "Validation failures occurred",
"invalidParams": [
{
"in": "headers",
"name": "X-RateLimitUnit",
"detail": "Must be either \"seconds\" or \"milliseconds\""
},
{
"in": "body",
"location": "$.authorities.roles[5]",
"type": "role",
"detail": "Role names must not contain spaces"
}
]
} Being able to easily perform this kind of thing would also then have the side effect of encouraging new users to use spring boot if they were previously using, say, some custom in-house validation with a JAX-RS servlet, as they will be able to still interoperate with existing services and conventions across their systems without having to perform a large rewrite of multiple services that rely on specific validation formatting. Additionally, it allows easier code-reuse when handling things like headers that store their representation in a second annotation. For example Mono<ResponseEntity<Void>> createUser(
@Valid @NotBlank @RequestHeader("Correlation-ID") String correlationId,
@Valid @RequestBody NewUser user
) {
...
} ...where you would want to show Of course, you can just validate most of these manually by creating the boilerplate, but that does feel like it kind of breaks the spirit of how spring is meant to be used, and can be a bit awkward for larger projects or groups of projects that want to be able to enforce consistency by extracting validation formatting to a common component, for example :-) |
Is there any update on this issue, or any plans that relate to this issue? |
I came upon this issue, because we noticed the inconsistencies in the Errors API. So in order to generate the same error response we came up with this quick'n'dirty solution:
It is far from perfect. It can be improved, if we reuse the code in Further details on the improved solution can be found in the following discussion: https://stackoverflow.com/questions/14893176/convert-jsr-303-validation-errors-to-springs-bindingresult |
The thing I wish we could do with this is be able to generate a consistent header name, cookie name, path variable, JSON path or xpath from the property path without adding massive amounts of reflective code. This would be really helpful for giving error messages to the user that reflect the payload shape rather than the spring internal representation which may differ with some Jackson annotations like unwrapping. {
"status": 400,
"title": "Bad Request",
"detail": "Validation failed",
"invalidParams": [
{
"location": "headers",
"name": "X-Request-ID",
"reason": "Must be a non-empty string"
},
{
"location": "body",
"name": "$.user.name",
"reason": "Must be specified"
}
]
} While I can write tonnes of code to try and cover the cases, it would be a great set of features to have internally. Error messages that use standard notations to refer to the request format would also prevent leaking internal implementation detail. The issue I currently see with the Spring 5.x.x branch is that since there is no consistent way to generate tidy validation without dealing with lots of edge cases, everyone seems to end up implementing their own solutions to this, so there is never a standard format that is widely accepted. Most enterprise companies won't use the current binding error responses on their public facing systems because the error messages can leak internal system details like the platform and libraries in use, or the error messages don't tarry up with the request format if snake_case, kebab-case, or custom named attributes are used. Businesses then consider that to be a security risk in some cases. (Security through obscurity isn't really an excuse in my opinion but opinions on the subject tend to differ wildly between people). One thing that does come to mind to make this easier is how JAXRS allows It would also allow body validation failures to be reported alongside header validation errors. @Validated
public record NewUserRequest(
@NotBlank @RequestHeader("X-Request-ID") String requestId,
@NotNull @Valid @RequestBody NewUser body
) { }
...
@PostMapping("/users")
public void newUser(@Valid @RequestBean NewUserRequest request) {
...
} From previous experience, BeanParam in JAXRS made consistent validation much simpler to handle. This would also potentially make using bean validation with functional routing easier too, as you could still produce an annotated payload and validate it. It isn't uncommon to want to move a lot of headers, path variables, the body, and other components around as one bean between components either. |
Thanks for all feedback @Sam-Kruglov and everyone else who commented. I've created #29825 as the main issue for validation improvements targeting 6.1 M1. Please, have a look at the details in the description of that issue, and provide feedback as needed. I'll leave this issue open, and schedule it for 6.1 M1 too, as it has a number of extra pointers such as the ones for |
A large part of this issue is superseded by the support for built-in web method validation in #30645 where 6b50b7b addresses #26219 (comment) 1e3161b addresses #26219 (comment) For Jackson's |
@ascopes thanks for the ideas to group errors by method parameter (headers, path variables, etc). That's easier now with #29825 where As for #26219 (comment), I see your point there although for error handling purposes you still need to differentiate which is request header, path variable, etc. In addition, the method validation support we are now adding should be able to address these needs for error handling. |
Affects: 5.3.1
Relates to #23107
Basically, I have the exact same feeling as the author did but he never clarified his point.
You can validate a DTO using
@Valid
annotation (whether it is built from a bunch of query parameters or from the body) but you can't validate literals like that. So, to validate a request parameter or a path parameter, one has to apply@Validated
annotation on the controller class level to enable the AOP proxy for method validation, then it will pick up any JSR 380 annotations.So, here is a little example. Given the following definition:
If we send the following requests, we will get these results:
Now, handling these 2 is not very nice although we want the same result in the end:
So, I see 3 inconveniences:
The text was updated successfully, but these errors were encountered: