【问题标题】:Validate JAXBElement in JPA/JAX-RS Web Service在 JPA/JAX-RS Web 服务中验证 JAXBElement
【发布时间】:2010-08-06 22:34:37
【问题描述】:

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

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

有人知道说明如何执行此操作的示例/教程吗?

谢谢!

【问题讨论】:

    标签: java validation jaxb jersey eclipselink


    【解决方案1】:

    您可以通过创建自定义 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);
            }
        }
    
    }
    

    【讨论】:

    • 最后我不需要针对架构进行验证,但如果我将来确实需要,这是很好的参考信息。
    【解决方案2】:

    我们可以更进一步,创建一个通用(抽象)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;
    }
    

    重写 validate 方法并用 @Provider 注释子类,我们就完成了。

    【讨论】:

    • 您好,谢谢。你能在这里回答一个相关的问题吗:stackoverflow.com/questions/12530155/…
    • 我一定遗漏了什么——这个类实际上并没有做任何验证?
    • @Deejay 这是很久以前的事了,我什至不再在同一个地方工作。但实际上,抽象类不进行验证。它只是创建类型并在子类中完成验证。显然现在看它,我可以想象调整抽象超类以设置模式并对其进行解组。这一切都取决于您的需求。
    猜你喜欢
    • 2023-03-10
    • 2012-07-27
    • 1970-01-01
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多