2014-08-28 38 views
9

在Jersey 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例如:

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()。如果沒有這個,實體被標記爲關閉,並在隨後的讀取嘗試中導致IllegalStateException異常。

+0

應該被接受的答案:) – user1046143 2015-02-25 08:48:28

+1

請注意'ContainerRequest'是一個Jersey類,不屬於JAX-RS標準的一部分。不幸的是,根據https://java.net/jira/browse/JERSEY-2664,僅憑JAX-RS是不可能的。 – Zero3 2015-12-15 18:26:38

2

表單POST參數在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 2014-09-05 20:30:25

+0

對不起,但它不工作在我的情況下,我也需要從'ContainerRequestContext'的形式參數' – HybrisFreelance 2014-09-24 12:10:23

+0

它使用ContainerRequestContext:https://jax-rs-spec.java.net/nonav/2.0-SNAPSHOT/apidocs/javax/ ws/rs/container/ContainerRequestContext.html#getEntityStream() – bhowden 2014-09-24 14:26:17

0

看着球衣1的實施,看起來好像是ContainerRequest#getEntity(Class)支持將實體流直接讀入Form類。

getEntity已更名爲readEntity在球衣2,所以下面可能工作(未經測試):

形式PARAMS = request.readEntity(Form.class);從球衣1個IMPL

建議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

由於ReaderWriter不是Jersey 2 API的一部分,因此這將不起作用。不確定是否有替換,遷移指南沒有提及。 – Mike 2014-09-17 14:57:43

0

這是一種閱讀表單實體而不依賴實現特定類的方法,即它可以與Jersey(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使用尚未具有Form類的JAX-RS API 2.0-m10。在這種情況下,人們可以簡單地用MultivaluedMap.class代替Form.class,這是以一些未檢查/原始類型警告的價格。