So you have a RESTful Java webapp built using Jersey, some POJO/JAXB models that you’d like to serialize using Jackson, and think it’d be sweet to validate models using annotations (JSR 303 – Bean Validation)? And you like your objects to be immutable (Effective Java Item #15). You’ve come to the right place. Hibernate Validator is the reference implementation for JSR 303 and arguably the best currently available, so we’ll use it along with Jersey and Jackson to form the backbone of this recipe.
The general idea behind the ValidatingJacksonJsonProvider
is that we can annotate any of the parameters used in our resource classes with @Valid
to have the object automatically validated during the Jackson JSON serialization process.
So, first, annotate your POJO/JAXB object with some constraints, like @Min(0)
and @NotEmpty
.
package com.mydomain.myapp.model; import javax.validation.constraints.Min; import com.fasterxml.jackson.annotation.JsonProperty; import org.hibernate.validator.constraints.NotEmpty; public class MyModel { private final long id; private final String name; public MyModel(@JsonProperty("id") long id, @JsonProperty("name") String name) { this.id = id; this.name = name; } public @Min(0) getId() { return id; } public @NotEmpty getName() { return name; } } |
Now we can validate MyModel
it in a Jersey resource class. For example, validating myModel before saving a new record to the database.
import javax.validation.Valid; import javax.ws.rs.POST; import com.mydomain.myapp.model.MyModel; public class MyResource { @POST public createMyModel(@Valid MyModel myModel) { ... } } |
The code to implement this recipe follows. Just add this alongside your other Jersey providers (e.g., in your resources
package). Its essentially a forwarding MessageBodyReader
, MessageBodyWriter
that delegates to the standard JacksonJsonProvider
, and hooks validation into the readFrom
call.
One way to make this smarter is to throw a RuntimeException that’s registered with a Jersey ExceptionMapper. Instead of always responding with a plain-text response, this will allow Jersey to handle content-negotiation and return an error response in the appropriate format. How else could you improve this technique?
Correct me if I’m wrong, but I don’t like injecting the JacksonProvider here. I would like to delegate the flow back to JAX-RS and not to Jackson in particular. This way this validation provider would be independent of the serializer.
Please check this post: http://weblogs.java.net/blog/ljnelson/archive/2010/04/28/pushing-jersey-limit
The author uses:
@Context
private Providers providers;
and then:
final MessageBodyWriter delegate = this.providers.getMessageBodyWriter(TheClass.class, TheClass.class, annotations, mediaType);
It appears to be a more generic and standard way to obtain the correct MessageBodyWriter. Using your approach you might be even overriding @Consumes or @Produces annotations and forcing the use of Jackson, but I’m not sure.
Thank you very much for your post.
Looking forward to have a “plug n play” JSR-303 provider for JAX-RS. : )
Miguel – the post you referenced was no longer in service (java.net, etc). I was so intrigued with your ideas I went searching for it. It’s now located at https://community.oracle.com/blogs/ljnelson/2010/04/28/pushing-jersey-limit.
@Miguel Very interesting. I didn’t consider delegating back to JAX-RS to make this more generic, but I believe that would work as well. It so happens that I only use JSON serialization in the project I extracted this from, so I never had the need… but I’ll definitely keep this in mind in the future.
Wow, that’s some hairy code in the post you linked. With mixed dependency-injection styles, ThreadLocals, and reflection-based instance creation, this would be crazy hard to unit test. But cool that it seems to work for the author, at least.
If I ever update this code to to support a generic serialization type, I’ll look at delegating back to JAX-RS and post an update here. Heck, I might just do it for fun… I sure hope that it’ll be easier than the JAXB wrapper hell he went through though.
PS – This validating provider still handles content-type negotiation appropriately because its only used for JSON requests. Notice the @Consumes and @Produces annotations on the ValidatingJacksonJsonProvider.
What I was trying to do wasn’t so simple. I ended up in an infinite loop because JAX-RS always returned my custom MessageBodyWriter. It makes sense since it always uses the first it finds. We need another thing that isn’t a MessageBodyWriter/Reader. These are for marshalling and unmarshalling purposes. JAX-RS 2.0 introduces Interceptors which maybe more suitable for cases like this (although JAX-RS 2.0 introduces Validation itself).
I’ll try to accomplish this task with some AOP, maybe.
Cool. Let me know how it goes. Sounds like something I’ll need to keep in mind if I use the delegate-to-JAXRS model.
Didn’t know that JAX-RS 2.0 introduced Validation. That’s sweet.
Check my question at SO for the outcome of my researches. Thank you for the inspiration. : )
http://stackoverflow.com/q/14218159/1137735
One more thing.
JSR-303 has support for groups. Example here: http://www.jroller.com/eyallupu/entry/jsr_303_beans_validation_using
This is important because you probably don’t want to do the same kind of validation across your entire application. An example is when you want to do a partial update (PUT). Or, for example, an id property must be null in a POST request but non-null everywhere else.
Your MessageBodyWriter/Reader could check the method for the group class present in the annotation @ValidateRequest(groups = PartialUpdateGroup.class) and then apply the appropriate validation. See example of this annotation usage here: @ValidateRequest(groups = PartialUpdateGroup.class).
Validation in REST WebServices appears to be quite challenging. : )
Haven’t found a complete example yet.
Just to let you know.
Hope I’ve been helpful. Thank you.
Oops, forgot the last link: http://docs.jboss.org/seam/3/rest/latest/reference/en-US/html/rest.validation.html
Why are you using Google Guice? BTW, Both delgate and validator are null in my case. So it does not work. I am using Jersey 1.x.
Billy – I’m using Google Guice to inject my dependencies, so that I don’t have to manually setup the object hierarchy. If you don’t like it, you can manually initialize the object. Those values should only be null if they are not injected correctly (though normally Guice throws an error if it can’t find a value to be injected).