2012-05-01 23 views
5

Estamos utilizando JAX-RS con algunas entidades bastante básicas POJO y tienen una serie de @GET y @POST métodos anotados que @Produce y @ConsumeMediaType.APPLICATION_JSON, MediaType.APPLICATION_XML. Nada espectacular.Validación práctica de datos JAXB con JAX-RS?

Pregunta: ¿Cuál es la mejor forma de validar los datos que entran?

No tenemos un esquema XML, aunque podría generar uno. Tendría que enganchar esto de alguna manera, que no parece terriblemente atractivo y aún no he encontrado un ejemplo conciso.

Podríamos usar "validación de bean", pero nuevamente estoy inseguro de cómo podría conectarlo e invocarlo.

Finalmente (creo) podríamos, por ejemplo, agregar algunos métodos isValidForXXX() a los POJO de la entidad y llamarlos siempre que nos entreguen una instancia.

Recomendaciones ¿alguien?

Respuesta

2

Si tiene un esquema XML, puede usar la validación JAXB dentro de un MessageBodyReader. Para un ejemplo concreto, vea mi respuesta a una pregunta similar.

ValidatingReader

A continuación se muestra una implementación esqueleto de MessageBodyReader que hace cuatro cosas: 1) Create a JAXBContext, 2) crear una instancia de Schema, 3) Establece el esquema en el Unmarshaller 4) Deshace el 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); 
     } 
    } 

} 
+4

¿Es realmente necesario que la cantidad de código?Realmente estaba buscando algo más simple ... – jmkgreen

+0

@jmkgreen - He actualizado mi respuesta con un ejemplo concreto. –

+2

@AbhishekRanjan es mejor decir "registrar clases de proveedor con contenedor de tiempo de ejecución". Para responder, debe comprender qué contenedor de tiempo de ejecución está utilizando y qué marcos de trabajo puede leer automáticamente (en) las clases de Proveedor, registrándolos en su contenedor si no lo hace automáticamente. – jmkgreen

0

El uso de un MessageBodyReader como sugiere la excelente @Blaise Doughan le da mucha flexibilidad. Obviamente, si ya tuviste @Consumes Annotation y tu clase con anotaciones JAXB, puedo ver cómo luce prolijo.

Realmente está a su nivel de comodidad. Noto que no definió un esquema XML, por lo que creó sus clases JAXB a mano, y parece lógico codificar la lógica de validación en las mismas clases (o un validador externo personalizado).

Personalmente, consideraría escribir ese esquema XML, especialmente si sus reglas de validación se expresan fácilmente. Podría ser una buena inversión para su proyecto: puede generar las clases JAXB automáticamente a partir de ese momento, y obtener la validación automáticamente, por lo que debe tener una mejor capacidad de mantenimiento en el futuro.

+1

Entonces, si escribo XSD y luego genroco clases JAXB, la validación se realizará automáticamente para JAX-RS? No necesito escribir MessageBodyReader yo mismo? – adi

+1

No sucederá automáticamente, pero el enfoque anterior de MessageBodyReader no es su única opción, en su lugar puede usar javax.ws.rs.ext.ContextResolver. Vea la excelente publicación en el blog en: http://sleeplessinslc.blogspot.com/2011/10/jersey-jax-rs-and-jaxb-schema.html –

+0

ok ... y ¿qué pasa con la validación de JSON? – adi

1

Estaba buscando lograr lo mismo que el cartel original. Dado que parecía haber un deseo de evitar un MessageBodyReader, pensé que compartiría esta solución, que solo usa el HttpServletRequest como parámetro y luego utiliza el método getInputStream para trabajar directamente con los datos.

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

El handleCustomerInteractionRequest método sería entonces manejar el flujo de servlets y validar el 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; 
} 
Cuestiones relacionadas