2014-08-28 4 views
9

В Джерси 1.x вы можете использовать ContainerRequest.getFormParameters() для фильтрации запросов на данные формы, но я не вижу очевидного эквивалента в Jersey 2.x. Я реализовал интерфейс ContainerRequestFilter, который дает мне доступ к ContainerRequestContext, но оттуда как можно получить данные формы?Фильтр Запрос Джерси по данным формы

Джерси пример 1.x:

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

    // examine form data and filter as needed 
    } 
} 

Джерси пример 2.x: параметры POST

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

ответ

15

После тонну поиска и проб и ошибок, я нашел подходящий способ сделать это в Джерси 2. Вы должны использовать тело объекта запроса вручную, но вы должны быть осторожны, чтобы сделать это таким образом, чтобы не препятствовать последующим фильтрам и ресурсам. Ниже приведен простой пример, который считывает объект в объект 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); 
      } 
     } 
    } 
} 

Ключ вызова bufferEntity(). Без этого объект помечен как закрытый и вызывает IllegalStateExceptions при любой последующей попытке чтения.

+0

Должен быть принятый ответ :) – user1046143

+1

Обратите внимание, что 'ContainerRequest' является классом Джерси, а не частью стандарта JAX-RS. К сожалению, это невозможно с JAX-RS в соответствии с https://java.net/jira/browse/JERSEY-2664. – Zero3

2

формы посылаются в теле запроса HTTP, так и с ContainerRequestContext вы можете сделать что-то вроде

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

Спасибо, вам кажется, что вам нужно напрямую управлять потоком сущности. Я просто хочу, чтобы команда Джерси оставила это там, чтобы помочь справиться с преобразованием потока, расшифровкой и построением многозначной карты. Похоже, это довольно распространенный случай использования, который требует наличия API. – Mike

+0

извините, но его не работает в моем случае, мне также нужен параметр param из 'ContainerRequestContext' – HybrisFreelance

+0

, он использует ContainerRequestContext: https://jax-rs-spec.java.net/nonav/2.0-SNAPSHOT/apidocs/javax/ ws/rs/container/ContainerRequestContext.html # getEntityStream() – bhowden

0

Глядя на реализацию трикотажа 1, кажется, что ContainerRequest#getEntity(Class) поддерживает чтение потока сущности непосредственно в класс Form.

getEntity был переименован в readEntity трикотажем 2, так что следующий может работать (непроверенные):

Форма PARAMS = request.readEntity (Form.class);

Предлагаемые осущ (украденные и слегка измененные) из джерси 1 осущ:

/** 
* 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

Это не сработает, поскольку ReaderWriter не является частью API Джерси 2.Не уверен, что есть замена, руководство по миграции не упоминает об этом. – Mike

0

Здесь вы можете прочитать объект формы, не полагаясь на конкретные классы реализации, то есть он будет работать как с Джерси (v2), так и с 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()); 
    } 
} 

Это хорошо работает и имеет преимущество использования только JAX-RS API, поэтому оно переносимо.

Обратите внимание, что CXF 2.x использует JAX-RS API 2.0-m10, который еще не имеет класса Form. В этом случае можно просто заменить Form.class на MultivaluedMap.class по цене некоторых предупреждений о непроверенных/необработанных типах.

Смежные вопросы