2014-08-28 14 views
9

In Jersey 1.x, è possibile utilizzare ContainerRequest.getFormParameters() per fare il filtraggio delle richieste sui dati del modulo, ma non vedo un equivalente evidente in Jersey 2.x. Ho implementato l'interfaccia ContainerRequestFilter che mi dà accesso a uno ContainerRequestContext, ma da lì come posso ottenere i dati del modulo?Filtro richiesta Jersey in base ai dati del modulo

Jersey esempio 1.x:

public class MyFilter implements ContainerRequestFilter { 
    public ContainerRequest filter(ContainerRequest request) { 
    Form f = request.getFormParameters(); 

    // examine form data and filter as needed 
    } 
} 

Jersey esempio 2.x: parametri POST

public class MyFilter implements ContainerRequestFilter { 
    public void filter(ContainerRequestContext context) { 
    // how do I get to the Form data now? 
    } 
} 

risposta

15

Dopo una tonnellata di ricerca e di tentativi ed errori, ho trovato un modo adatto per fare questo in Jersey 2. Devi consumare manualmente il corpo dell'entità della richiesta, ma devi fare attenzione a farlo in un modo che non impedisca ai successivi filtri e risorse di consumarlo. Di seguito è riportato un semplice esempio che legge l'entità in un oggetto Form:

@Provider 
public class FormDataFilter implements ContainerRequestFilter 
{ 
    @Override 
    public void filter(ContainerRequestContext requestContext) throws IOException 
    { 
     if (requestContext instanceof ContainerRequest) 
     { 
      ContainerRequest request = (ContainerRequest) requestContext; 

      if (requestContext.hasEntity() 
       && MediaTypes.typeEqual(MediaType.APPLICATION_FORM_URLENCODED_TYPE,request.getMediaType())) 
      { 
       request.bufferEntity(); 
       Form f = request.readEntity(Form.class); 
      } 
     } 
    } 
} 

La chiave chiama bufferEntity(). Senza questo, l'entità viene contrassegnata come chiusa e causa IllegalStateException in qualsiasi successivo tentativo di lettura.

+0

Dovrebbe essere la risposta accettata :) – user1046143

+1

Si noti che 'ContainerRequest' è una classe Jersey e non fa parte dello standard JAX-RS. Sfortunatamente, questo non è possibile solo con JAX-RS secondo https://java.net/jira/browse/JERSEY-2664. – Zero3

2

form sono inviati nel corpo della richiesta http, quindi con ContainerRequestContext si può fare qualcosa di simile

String q = IOUtils.toString(context.getEntityStream(), Charsets.UTF_8); 
String[] params = q.split("&"); 
Map<String, String> map = new HashMap<>(); 
for (String param : params) 
{ 
    String name = param.split("=")[0]; 
    String value = param.split("=")[1]; 
    map.put(name, value); 
} 
+1

Grazie, sembra che tu debba manipolare direttamente il flusso di entità. Vorrei solo che il team di Jersey lo avesse lasciato lì per aiutare a gestire la conversione del flusso, la decodifica e la creazione di una mappa multivalore. Sembra un caso d'uso piuttosto comune che merita di essere nell'API. – Mike

+0

scusate ma, non funziona nel mio caso, ho anche bisogno del param del form da 'ContainerRequestContext' – HybrisFreelance

+0

usando ContainerRequestContext: https://jax-rs-spec.java.net/nonav/2.0-SNAPSHOT/apidocs/javax/ ws/rs/container/ContainerRequestContext.html # getEntityStream() – bhowden

0

Guardando l'implementazione della jersey 1, sembra che ContainerRequest#getEntity(Class) supporta la lettura dello stream di entità direttamente in una classe Form.

getEntity è stato rinominato in readEntity in jersey 2, quindi il seguente potrebbe funzionare (non testata):

Modulo params = request.readEntity (Form.class);

impl suggerite (rubate e leggermente modificati) dal jersey 1 impl:

/** 
* Stolen from the jersey 1 impl 
*/ 
public static Form getFormParameters(ContainerRequest request) { 
    if (MediaTypes.typeEqual(MediaType.APPLICATION_FORM_URLENCODED_TYPE, request.getMediaType())) { 
     InputStream in = request.getEntityStream(); 
     if (in.getClass() != ByteArrayInputStream.class) { 
      // Buffer input 
      ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
      try { 
       ReaderWriter.writeTo(in, baos); 
      } catch (IOException e) { 
       throw new IllegalArgumentException(e); 
      } 

      in = new ByteArrayInputStream(baos.toByteArray()); 
      request.setEntityStream(in); 
     } 

     ByteArrayInputStream bais = (ByteArrayInputStream) in; 
     Form f = request.readEntity(Form.class); 
     bais.reset(); 
     return f; 
    } else { 
     return new Form(); 
    } 
} 
+0

Questo non funzionerà poiché ReaderWriter non fa parte dell'API di Jersey 2.Non sono sicuro se c'è una sostituzione, la guida alla migrazione non ne parla. – Mike

0

Ecco un modo per leggere l'entità del modulo senza fare affidamento su classi specifiche di implementazione, cioè funzionerà sia con Jersey (v2) che con CXF (v3).

@Provider 
public class AFilter implements ContainerRequestFilter { 

    @Context 
    private Providers providers; 

    @Override 
    public void filter(ContainerRequestContext request) throws IOException { 
     if (!request.hasEntity() || !MediaTypes.typeEqual(APPLICATION_FORM_URLENCODED_TYPE, request.getMediaType())) { 
      // if not a form ... 
      return; 
     } 

     ByteArrayInputStream resettableIS = toResettableStream(request.getEntityStream()); 

     Form form = providers.getMessageBodyReader(Form.class, Form.class, new Annotation[0], APPLICATION_FORM_URLENCODED_TYPE) 
          .readFrom(Form.class, Form.class, new Annotation[0], APPLICATION_FORM_URLENCODED_TYPE, null, resettableIS); 

     // do something with Form 

     resettableIS.reset(); 
     request.setEntityStream(resettableIS); 
    } 

    @Nonnull 
    private ByteArrayInputStream toResettableStream(InputStream entityStream) throws IOException { 
     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
     byte[] buffer = new byte[1024]; 
     int len; 
     while ((len = entityStream.read(buffer)) > -1) { 
      baos.write(buffer, 0, len); 
     } 
     baos.flush(); 
     return new ByteArrayInputStream(baos.toByteArray()); 
    } 
} 

Questo funziona bene e ha il vantaggio di utilizzare solo API JAX-RS quindi è portatile.

Si noti tuttavia che CXF 2.x utilizza JAX-RS API 2.0-m10 che non ha ancora la classe Form. In questo caso si può semplicemente sostituire Form.class con lo MultivaluedMap.class al prezzo di alcuni avvisi di tipo non controllato/non elaborato.

Problemi correlati