Lets say I define a POJO with parameters that is passed to a REST call
class MyVO {
#NotNull
#PathParam("name")
private String name;
#NotNull
#PathParam("age")
private Integer age;
// getters and setters
}
public class RESTclass {
public postData( #Form MyVO vo ) {
}
}
It automatically binds the objects in MyVO. But where do I get the validation errors?
Does it trigger the validation during binding? If not, how to trigger the validations?
Spring does all these well. It has BindingResult parameter that you can inject.
What is the equivalent here?
Any idea?
RestEasy Versions Prior to 3.0.1.Final
For bean validation 1.0, Resteasy has a custom validation provider that uses hibernate's bean validator implementation under the covers.
In order to get validation up and running in Resteasy you need to do the following:
Add the resteasy-hibernatevalidator-provider dependency to your project. Here is the maven pom entry if you are using maven:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-hibernatevalidator-provider</artifactId>
<version>${resteasy.version}</version>
</dependency>
Annotate your resource classes where you want validation to occur with the #ValidateRequest annotation.
#Named
#Path("/users")
#ValidateRequest
public class UserResource extends BaseResource
{
#POST
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response createUser(#Valid User user)
{
//Do Something Here
}
}
Resteasy will automatically detect the HibernateValidatorAdapter on the classpath and begin invoking bean validation.
Create an ExceptionMapper<MethodConstraintViolationException> implementation to handle the validation errors.
Unlike in Spring where you have to check the BindingResult, when validation errors are encountered in Resteasy the hibernate validator will throw a MethodConstraintViolationException. The MethodConstraintViolationException will contain all of the validation errors within it.
#Provider
public class MethodConstraintViolationExceptionMapper extends MyBaseExceptionMapper
implements ExceptionMapper<MethodConstraintViolationException>
{
#Override
public Response toResponse(MethodConstraintViolationException exception)
{
//Do Something with the errors here and create a response.
}
}
RestEasy Version 3.0.1.Final
The latest version of Resteasy is now supporting bean validation spec 1.1 and has changed the api and exceptions thrown.
Instead of the resteasy-hibernatevalidator-provider you are going
to need the resteasy-validator-provider-11 dependency.
You will not need to add #ValidateRequest to your resource classes
as validation testing is turned on by default with
resteasy-validator-provider-11.
Instead of throwing a MethodConstraintViolationException when
violations are detected, an instance of RESTEasyViolationException
will be thrown.
Documentation: 3.0.1.Final Validation Documentation
Related
I have a Spring Boot Controller with an endpoint that accepts the following Configuration class (as json). The class has the param maxIterations and has a #Size annotation set to 9000.
...
import javax.validation.constraints.Max;
import javax.validation.constraints.Size;
public class Configuration {
// Max iterations
#Size(max = 9000)
private Integer maxIterations;
...
}
The problem is that when making the POST call to that endpoint with the following json it does not return an error or warning stating the the maxIterations parameter is higher than 9000.
{
"maxIterations": 15000
}
This is my controller:
#PostMapping()
#ResponseStatus(HttpStatus.CREATED)
public String doSomething(#RequestBody Configuration configuration) {
...
}
What can be the issue?
As mentioned in the comments, you should be using #Max(9000) as #Size should only be used for arrays, Strings, collections and maps.
public class Configuration {
#Max(9000)
private Integer maxIterations;
// ...
}
Additionally, you should use the #Valid annotation within your controller so that the bean validator knows that there are constraints within the Configuration class, for example:
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public String doSomething(#Valid #RequestBody Configuration configuration) {
// ...
}
This only works if you have a proper implementation of the Java Validation API on your classpath. This can be done by adding the following dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Before Spring boot 2.3, this was automatically included within the spring-boot-starter-web dependency, but now you have to manually include the dependency.
I am doing a spring boot rest application with a custom exception mapping. The exception mapping is irrelevant, but I wanted to use hibernate validator in version 4 ((because I have to use java validation api version 1.0).
Now I am initializing the validators in #configuration class:
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
#Bean
MethodValidationPostProcessor methodValidationPostProcessor(Validator validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator);
return processor;
}
And I create a resource:
#Path("/validation")
#Produces("application/json")
#Validated
public interface ValidationTestResource {
#POST
#Path("/test")
#Validated
public void translatedAndMappedException(#RequestBody #Valid
ValidationTestDto testDto);
}
}
The resource works. And the validation works. But whenever I violate the constraints on ValidationTestDto I get an exception. However this is org.hibernate.validator.method.MethodConstraintViolationException and it only specified which method and which parameter failed to validate (translatedAndMappedException, argument testDto). This makes it kind of useless to return the violated field list, as such list is not present.
But If I use hibernate-validator version 5+ I get a proper javax.validation.ConstraintViolationException and I can extract a full list of fields that were not validated.
The question is:
1.If I am simply doing something wrong, and Hibernate validator 4 is like that.
2.If I can use something from spring to validate and forget about hibernate-validator that would do it properly?
Spring provides its own validation interfaces, so you can try using those directly. The documentation is here. You can use a BindingResult to see the details of the validation errors that are produced.
I'm using CDI in a Jersey app. On root resources, CDI injection works as expected, but whenever I return a sub-resource, the CDI injection sources are not available.
My root resource with sub-resource locator:
#Path("")
public class MyResource {
#Inject #Named("name") // works
private String name;
#Context
private ResourceContext context;
#Path("test2")
public Object test2() {
return MySubResource.class;
//return context.getResource(MySubResource.class); // this does not work either
}
}
The sub-resource:
public class MySubResource {
#Inject #Named("name") // error
private String name;
#GET
public Response test() {
return Response.ok("Name in sub resource: " + name).build();
}
}
Error:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=String,parent=MySubResource,qualifiers={#javax.inject.Named(value=name)},position=-1,optional=false,self=false,unqualified=null,1235803160)
I'm using org.glassfish.jersey.ext.cdi:jersey-cdi1x and Weld dependencies, running on Undertow, with the Weld servlet listener added to the deployment.
Again, the same injection on the root resource does work. The #Named("name") String is produced by an #ApplicationScoped producer.
Is this not supposed to work? What am I missing?
Minimal example Maven project available here:
https://gitlab.com/Victor8321/jersey-sub-resource-cdi
Note: An open issue for this exists, but not sure what the official stance on this is: https://java.net/jira/browse/JERSEY-3184
As pointed out in https://github.com/eclipse-ee4j/jersey/issues/3456, adding a dummy #Path("xyz") to the sub-resource class is a "fix". However, that exposes your sub-resource under the dummy path.
Injecting an instance just through CDI works as well (#Inject Instance<MySubResource> ..), but then Jersey-managed resources aren't available for injection, such as #Context HttpServletRequest.
I've found 2 other approaches that fully work (both CDI injection and JAX-RS injection) and have no side effects (as with #Path):
Annotate the sub resource class with #Provider.
register() the sub resource class in the ResourceConfig (or Application).
Both approaches seem to work because they make Jersey - and in turn, CDI - aware of the class.
Note: I've updated my example project accordingly for future reference.
I'm trying to validate my JPA Entity with #Valid like this:
public static void persist(#Valid Object o)
It worked fine for a while but now it stopped working and I'm not sure why. I tried to do it manually inside the persist method, and it works as expected:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(o);
if (!constraintViolations.isEmpty()) {
throw new ConstraintViolationException(constraintViolations);
}
What could be happening or how can I debug this?
Method Validations is only available starting in bean validation v 1.1 (e.g. hibernate validator 5.x impl) which is part of Java EE 7 only. On top of that to have it working without extra specific BV code your method must be part of a component which is integrated with bean validation (eg. CDI Beans, JAX-RS Resource). Your custom code works because you do not use method validation but rather BV constraints which are defined directly on your object.
Won't work on arbitrary services. In Jersey it will only work for resource methods. So validate the incoming DTO in your resource method.
#POST
public Response post(#Valid SomeDTO dto) {}
See more at Bean Validation Support
UPDATE
So to answer the OP's comment about how we can make it work on arbitrary services, I created a small project that you can plug and play into your application.
You can find it on GitHub (jersey-hk2-validate).
Please look at the tests in the project. You will find a complete JPA example in there also.
Usage
Clone, build, and add it your Maven project
public interface ServiceContract {
void save(Model model);
}
public class ServiceContractImpl implements ServiceContract, Validatable {
#Override
public void save(#Valid Model model) {}
}
Then use the ValidationFeature to bind the service
ValidationFeature feature = new ValidationFeature.Builder()
.addSingletonClass(ServiceContractImpl.class, ServiceContract.class).build();
ResourceConfig config = new ResourceConfig();
config.register(feature);
The key point is to make your service implementation implement Validatable.
The details of the implementation are in the README. But the gist of it is that it makes use of HK2 AOP. So your services will need to be managed by HK2 for it to work. That is what the ValidationFeature does for you.
Problem Background
I have a Resteasy service that uses Spring through Resteasy's SpringContextLoaderListener. This is built on Resteasy version 3.0-beta-6.
I would like to use bean validation on the incoming requests, but I can not get Resteasy to call the validator. It acts like there is no validation configured and simply passes the method the invalid input object.
Question
How do I enable bean validation in Resteasy?
What I've Tried
I have done the following:
Annotated my resource class with #ValidateRequest
Annotated the method parameter with #Valid
Annotated the constraints on my input class.
Added a dependency on resteasy-hibernatevalidator-provider
Resource:
#Named
#Path("users")
#ValidateRequest
public class UserResource
{
/**
*
* #param user
*
* curl -x POST http://localhost:7016/api/1.0/users
*
*/
#POST
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response createUser(#Valid User user)
{
//User creation logic here.
}
}
User POJO:
#JsonPropertyOrder({
"user_id",
"user_name",
"email"
})
public class User
{
#JsonProperty("user_id")
private Long userId;
#JsonProperty("user_name")
#NotNull(message = "Username must be provided")
private String username;
#Email(message = "Invalid email address.")
private String email;
//Getters and Setters Removed for Brevity
}
POM Entry for Validation:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-hibernatevalidator-provider</artifactId>
<version>${resteasy.version}</version>
</dependency>
The resteasy-hibernatevalidator-provider dependency brings in the HibernateValidatorContextResolver and its associated HibernateValidatorAdapter.
Update (6/18/2013):
I reverted the Resteasy version in my pom to 2.3.5.Final and bean validation started working without any code changes.
Running with Resteasy '3.0.6.Final' and Spring '4.1.0.RELEASE'.
The 'resteasy-hibernatevalidator-provider' does not evaluate the #Valid annotated params. Using the 'resteasy-validator-provider-11' makes everything work and as a bonus is using Hiberbate validator '5.0.1.Final' instead of needing a Hibernate validator version 4 when using the 'resteasy-hibernatevalidator-provider'.
Have you done this:
Providing a ValidatorAdapter to RESTEasy
RESTEasy will try to obtain an implementation of ValidatorAdapter through a ContextResolver provider in the classpath. We can provide RESTEasy with an implementation like follow:
#Provider
public class MyValidatorContextResolver implements ContextResolver<ValidatorAdapter> {
#Override
public ValidatorAdapter getContext(Class<?> type) {
return new MyValidator();
}
}
Make sure you have the META-INF/services/javax.ws.rs.Providers defined in your war.
You can use the hibernate validator provided with resteasy-3.0.
Check chapter 48, of the resteasy documentation :
http://docs.jboss.org/resteasy/docs/3.0.0.Final/userguide/html_single/index.html#JBoss AS 6