在JPA/JAX-RS Web Service中验证JAXBElement

sdo*_*oca 10 java validation jaxb jersey eclipselink

我有一个JAX-RS webservice(Jersey),它是JPA(EclipseLink)实体的CRUD接口.我的实体是从数据库表中自动生成的,我使用JAXB注释对它们进行了注释,以便可以将它们编组到/从XML进行编组/解组.我的资源方法将JAXBElement对象作为必需的参数.

我没有XSD,但是,我愿意写一个来验证请求中收到的XML.但是,我不知道如何启动验证.Jersey会自动处理编组/解组,我发现的有关验证的任何参考都是在该级别完成的.

有人知道一个示例/教程,说明如何做到这一点?

谢谢!

bdo*_*han 15

您可以通过创建自定义MessageBodyReader来处理此问题.以下示例基于Customer模型:

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URL;

import javax.ws.rs.Consumes;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

@Provider
@Consumes("application/xml")
public class ValidatingReader implements MessageBodyReader<Customer> {

    @Context
    protected Providers providers;

    private Schema schema;

    public ValidatingReader() {
        try {
            SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            URL schemaURL = null;
            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 {
            JAXBContext jaxbContext = null;
            ContextResolver<JAXBContext> resolver = providers.getContextResolver(JAXBContext.class, arg3);
            if(null != resolver) {
                jaxbContext = resolver.getContext(arg0);
            }
            if(null == jaxbContext) {
                jaxbContext = JAXBContext.newInstance(arg0);
            }
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            unmarshaller.setSchema(schema);
            return (Customer) unmarshaller.unmarshal(arg5);
        } catch(JAXBException e) {
            throw new RuntimeException(e);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)


GWT*_*bie 9

我们可以更进一步,创建一个通用的(抽象的)ValidatingReader,它可以在需要时进行细分.这就是我所做的,感谢Blaise的想法:

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Providers;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

public abstract class AbstractValidatingReader<T> implements
    MessageBodyReader<T> {

@Context
protected Providers providers;

@SuppressWarnings("unchecked")
@Override
public boolean isReadable(Class<?> arg0, Type arg1, Annotation[] arg2,
        MediaType arg3) {

    Class<T> readableClass = (Class<T>) ((ParameterizedType) getClass()
            .getGenericSuperclass()).getActualTypeArguments()[0];
    return arg0 == readableClass;
}

@SuppressWarnings("unchecked")
@Override
public T readFrom(Class<T> arg0, Type arg1, Annotation[] arg2,
        MediaType arg3, MultivaluedMap<String, String> arg4,
        InputStream arg5) throws IOException, WebApplicationException {

    T type = null;
    JAXBContext jaxbContext = null;
    ContextResolver<JAXBContext> resolver = providers.getContextResolver(
            JAXBContext.class, arg3);
    try {

        if (resolver != null) {
            jaxbContext = resolver.getContext(arg0);
        }

        if (jaxbContext == null) {
            jaxbContext = JAXBContext.newInstance(arg0);

        }
        type = (T) jaxbContext.createUnmarshaller().unmarshal(arg5);
        validate(type);

    } catch (JAXBException e) {
        throw new WebApplicationException(
                Response.Status.INTERNAL_SERVER_ERROR);
    }

    return type;
}

protected abstract void validate(T arg0) throws WebApplicationException;
}
Run Code Online (Sandbox Code Playgroud)

覆盖validate方法并使用@Provider注释子类,我们就完成了.