6

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?

dur
  • 15,689
  • 25
  • 79
  • 125
jmkgreen
  • 1,633
  • 14
  • 22

3 Answers3

2

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);
        }
    }

}
Community
  • 1
  • 1
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • 2
    @AbhishekRanjan better to say "register provider classes with runtime container". To answer you need to understand which runtime container you are using and what if any frameworks may be automatically reading (at)Provider classes, registering them in your container if non do automatically. – jmkgreen Nov 20 '15 at 10:08
1

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;
} 
Mike Barlotta
  • 715
  • 4
  • 16
0

Using a MessageBodyReader as the excellent @Blaise Doughan suggests gives you lots of flexibility. Obviously, if you had you @Consumes Annotation and your JAXB-annotated class already, I can see how this looks verbose.

It's really up to your comfort level. I note that you didn't define an XML schema, so you created your JAXB classes by hand, and it seems logical to code the validation logic in the same classes (or an external custom validator).

I would personally consider writing that XML Schema, especially if your validation rules are easily expressed. It could be a good investment for your project: you can generate the JAXB classes automatically from then on, and get the validation automatically, so you should have a better maintainability going forward.

Patrice M.
  • 4,209
  • 2
  • 27
  • 36
  • 1
    So if I write XSD and then genrate JAXB classes, the validation will happen automatically for JAX-RS? I dont need to write MessageBodyReader myself?? – adi Jul 26 '12 at 11:40
  • 1
    It will not happen automatically, but the MessageBodyReader approach above is not your only option, instead you can use a javax.ws.rs.ext.ContextResolver. See the excellent blog post at: http://sleeplessinslc.blogspot.com/2011/10/jersey-jax-rs-and-jaxb-schema.html – Patrice M. Jul 27 '12 at 12:08
  • I'm afraid I'm not an expert in terms of JSON validation... anyone cares to comment ? – Patrice M. Jul 29 '12 at 02:37
  • 1
    Jackson json validator library is available for Json validation. – Abhishek Ranjan Nov 03 '15 at 10:21