We are using JAX-RS with some fairly basic POJO entities and have a number of @GET
and @POST
annotated methods that @Produce
and @Consume
MediaType.APPLICATION_JSON
, MediaType.APPLICATION_XML
. Nothing spectacular.
Question I have is how best should I validate the data coming in?
We do not have an XML Schema although I could generate one. I would need to hook this in somehow which doesn't look terribly appealing and I have not found a concise example yet.
We could use "bean validation" but I am again unsure how I might hook this up and invoke it.
Finally (I think) we could for example add some isValidForXXX()
methods to the entity POJOs and call them whenever we have an instance handed in to us.
Recommendations anyone?
Validation is the process of verifying that some data obeys one or more pre-defined constraints. It is, of course, a very common use case in most applications. The Java Bean Validation framework (JSR-380) has become the de-facto standard for handling this kind of operations in Java.
Your JAX-RS application can use the JAXB objects to manipulate XML data. JAXB objects can be used as request entity parameters and response entities. The JAX-RS runtime environment includes standard MessageBodyReader and MessageBodyWriter provider interfaces for reading and writing JAXB objects as entities.
JAXB definitionsMarshalling is the process of transforming Java objects into XML documents. Unmarshalling is the process of reading XML documents into Java objects. The JAXBContext class provides the client's entry point to the JAXB API. It provides API for marshalling, unmarshalling and validating.
Java Architecture for XML Binding (JAXB) is an XML-to-Java binding technology that simplifies the development of web services by enabling transformations between schema and Java objects and between XML instance documents and Java object instances.
If you have an XML schema then you could use JAXB validation inside a MessageBodyReader
. For a concrete example see my answer to a similar question.
ValidatingReader
Below is a bare bones implementation of MessageBodyReader
that does four things: 1) Create a JAXBContext
, 2) Create an instance of Schema
, 3) Sets the schema on the Unmarshaller
4) Unmarshals the InputStream
.
package org.example;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URL;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import javax.xml.XMLConstants;
import javax.xml.bind.*;
import javax.xml.validation.*;
@Provider
@Consumes("application/xml")
public class ValidatingReader implements MessageBodyReader<Customer> {
@Context
protected Providers providers;
private Schema schema;
private JAXBContext jaxbContext;
public ValidatingReader() {
try {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
URL schemaURL = null; // URL for your XML schema
schema = sf.newSchema(schemaURL);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
public boolean isReadable(Class<?> arg0, Type arg1, Annotation[] arg2, MediaType arg3) {
return arg0 == Customer.class;
}
public Customer readFrom(Class<Customer> arg0, Type arg1, Annotation[] arg2, MediaType arg3, MultivaluedMap<String, String> arg4, InputStream arg5)
throws IOException, WebApplicationException {
try {
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema(schema);
return (Customer) unmarshaller.unmarshal(arg5);
} catch(JAXBException e) {
throw new RuntimeException(e);
}
}
}
Was looking to accomplish the same thing as the original poster. Since there seemed to be a desire to avoid a MessageBodyReader
I thought I would share this solution, which just uses the HttpServletRequest
as a parameter and then uses the getInputStream
method to directly work with the data.
@POST
@Path("/myPath")
@Consumes(MediaType.APPLICATION_XML)
public Response saveCustomerInteraction(@Context HttpServletRequest httpRequest) {
Response response = null;
try {
CustomerInteraction ci = this.handleCustomerInteractionRequest(httpRequest);
if (ci!=null){
// save your data or whatever your app does
}
else {
response = Response.status(Status.BAD_REQUEST).entity("invalid XML").build();
}
}
catch(Exception e){
log.error(e.getMessage(),e);
response = Response.serverError().entity(e.getMessage()).build();
}
return response;
}
The handleCustomerInteractionRequest
method would then handle the servlet stream and validate the XML
protected CustomerInteraction handleCustomerInteractionRequest(HttpServletRequest request){
CustomerInteraction ci = null;
try {
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(schemaUrl);
JAXBContext jaxbContext = JAXBContext.newInstance(CustomerInteraction.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema(schema);
ci = (CustomerInteraction) unmarshaller.unmarshal(request.getInputStream());
}
catch (Exception e){
// log error and return a null (or whatever you want to do with the error)
}
return ci;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With