1

I have a Jersey-based application and I want to add validation to input.

@POST
@Produces(APPLICATION_JSON) @Consumes(APPLICATION_JSON)
public SomeResponce myMethod(@Valid MyBean myBean)

Problem is that my beans are generated by protostuff and I cant add validation annotations. I've found the way how to validate beans without annotation/xml: http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-programmatic-api

But Im very unfamiliar with Jersey and cant find in docs how to add this custom validators to Jersey without use of annotations in bean. Is any way to do it?

Jersey version - 2.8

Serge Harnyk
  • 1,279
  • 10
  • 19

2 Answers2

1

An easy way (and possible with jersey too) is to configure hibernate validator via XML. Place the validation.xml file in the META-INF folder and create constraint-mappings in the referenced xml file:

src/main/resources/META-INF/validation.xml (the validation.xml must be in the folder META-INF in your classpath) :

<validation-config
        xmlns="http://jboss.org/xml/ns/javax/validation/configuration" version="1.1">
    <constraint-mapping>META-INF/mapping.xml</constraint-mapping>
</validation-config>

src/main/resources/META-INF/mapping.xml :

<constraint-mappings
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.1.xsd"
        xmlns="http://jboss.org/xml/ns/javax/validation/mapping" version="1.1">

    <default-package>com.example</default-package>
    <bean class="MyBean" ignore-annotations="true">
        <field name="name">
            <constraint annotation="javax.validation.constraints.Size">
                <element name="max">5</element>
            </constraint>
            <constraint annotation="javax.validation.constraints.NotNull">
            </constraint>
        </field>
    </bean>
</constraint-mappings>

src/main/java/com/example/MyBean.java :

package com.example;

public class MyBean {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

src/main/java/com/example/MyResource.java :

package com.example;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

@Path("/api")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class MyResource {

    @POST
    public String postIt(@Valid MyBean bean) {
        return bean.getName();
    }
}
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Meiko Rachimow
  • 4,664
  • 2
  • 25
  • 43
1

Use a MessageBodyReader to provide "custom" de-serialization. This customization will simply provide an XSD schema to the standard deserializer (unmarshaller), which will validate all your types. This will happen BEFORE getting to your method. Here is a quick example:

@Provider
@Consumes("application/xml")
public class MyMsgBodyReader implements MessageBodyReader {

@Override
public boolean isReadable(Class type, java.lang.reflect.Type genericType, Annotation[] annotations, MediaType mediaType) {
    return type.isAnnotationPresent(XmlRootElement.class);
}

@Override
public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {

    try {
        JAXBContext ctx = JAXBContext.newInstance(type);
        Unmarshaller unmarhsaller = ctx.createUnmarshaller();

        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        URL schemaFile = HelloWolrd.class.getResource("XSD_FILE_LOCATION");
        if (schemaFile != null) {
            Schema schema = sf.newSchema(schemaFile);
            unmarhsaller.setSchema(schema);
        }
        else {
           System.out.println("not validating xml");
        }

        return unmarhsaller.unmarshal(entityStream);
    }
    catch (JAXBException e) {
        logger.log(e.toString(), Level.WARNING);
    }

    return null;
    }
}

with this addition, you can now have simply:

@Path("HelloWolrd")
public class HelloWorld{

    @POST
    @Produces(MediaType.APPLICATION_XML)
    @Consumes(MediaType.APPLICATION_XML)
    public Response query(HelloRequest rqst) {
        System.out.println(rqst.getSomeField()); // <-- ALREDAY validated
    } 
}
KAGR
  • 11
  • 3