如何在请求filter中获取表单参数

我正在尝试在请求filter中获取请求的表单参数:

@Override public ContainerRequest filter(final ContainerRequest request) { final Form formParameters = request.getFormParameters(); //logic return request; } 

但是,表格似乎总是空的。 HttpRequestContext.getFormParameters()文档说:

获取请求实体的表单参数。

此方法将确保缓冲请求实体,以便应用程序可以使用它。

返回:表单参数,如果有请求实体且内容类型为“application / x-www-form-urlencoded”,否则将返回不包含参数的实例。

我的资源是用@Consumes("application/x-www-form-urlencoded")注释的,虽然它在请求filter之后才会匹配 – 这就是为什么这不起作用?

我尝试做一些研究,但未能找到任何确凿的证据certificate这是否可行。 有这个为期4年的讨论 ,Paul Sandoz说:

如果您正在使用Jerseyfilter或使用HttpRequestContext您可以获得如下表单参数:[到Jersey 1.1.1的断开链接HttpRequestContext.getFormParameters ]

我还发现了这个有关如何在请求filter中获取multipart / form-data表单字段的3年讨论 。 其中,Paul Sandoz使用以下代码:

 // Buffer InputStream in = request.getEntityInputStream(); if (in.getClass() != ByteArrayInputStream.class) { // Buffer input ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { ReaderWriter.writeTo(in, baos); } catch (IOException ex) { throw new ContainerException(ex); } in = new ByteArrayInputStream(baos.toByteArray()); request.setEntityInputStream(in); } // Read entity FormDataMultiPart multiPart = request.getEntity(FormDataMultiPart.class); 

我尝试使用Form模拟该方法,但request.getEntityInputStream()的结果始终是空流。 看看getFormParameters的来源 ,该方法实际上已经做了同样的事情:

 @Override public Form getFormParameters() { if (MediaTypes.typeEquals(MediaType.APPLICATION_FORM_URLENCODED_TYPE, getMediaType())) { InputStream in = getEntityInputStream(); if (in.getClass() != ByteArrayInputStream.class) { // Buffer input ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try { ReaderWriter.writeTo(in, byteArrayOutputStream); } catch (IOException e) { throw new IllegalArgumentException(e); } in = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); setEntityInputStream(in); } ByteArrayInputStream byteArrayInputStream = (ByteArrayInputStream) in; Form f = getEntity(Form.class); byteArrayInputStream.reset(); return f; } else { return new Form(); } } 

在我开始之前,我无法弄清楚实体输入流是什么。 泽西岛的某些东西必须消耗它,因为forms参数后来被传递到资源方法中。 我在这里做错了什么,或者这是不可能的(以及为什么)?

编辑:以下是发送请求的示例:

 POST /test/post-stuff HTTP/1.1 Host: local.my.application.com:8443 Cache-Control: no-cache Content-Type: application/x-www-form-urlencoded form_param_1=foo&form_param_2=bar 

这是(有点多余的) 请求记录 :

 INFO: 1 * Server in-bound request 1 > POST https://local.my.application.com:8443/test/post-stuff 1 > host: local.my.application.com:8443 1 > connection: keep-alive 1 > content-length: 33 1 > cache-control: no-cache 1 > origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm 1 > user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36 1 > content-type: application/x-www-form-urlencoded 1 > accept: */* 1 > accept-encoding: gzip,deflate,sdch 1 > accept-language: en-US,en;q=0.8 1 > cookie: [omitted] 1 > 

以下是该请求的响应标头,包括Jersey Trace :

 Content-Type →application/json;charset=UTF-8 Date →Fri, 09 Aug 2013 18:00:17 GMT Location →https://local.my.application.com:8443/test/post-stuff/ Server →Apache-Coyote/1.1 Transfer-Encoding →chunked X-Jersey-Trace-000 →accept root resource classes: "/post-stuff" X-Jersey-Trace-001 →match path "/post-stuff" -> "/post\-stuff(/.*)?", [...], "(/.*)?" X-Jersey-Trace-002 →accept right hand path java.util.regex.Matcher[pattern=/post\-stuff(/.*)? region=0,11 lastmatch=/post-stuff]: "/post-stuff" -> "/post-stuff" : "" X-Jersey-Trace-003 →accept resource: "post-stuff" -> @Path("/post-stuff") com.application.my.jersey.resource.TestResource@7612e9d2 X-Jersey-Trace-004 →match path "" -> "" X-Jersey-Trace-005 →accept resource methods: "post-stuff", POST -> com.application.my.jersey.resource.TestResource@7612e9d2 X-Jersey-Trace-006 →matched resource method: public javax.ws.rs.core.Response com.application.my.jersey.resource.TestResource.execute(java.lang.String,java.lang.String) X-Jersey-Trace-007 →matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@b98df1f X-Jersey-Trace-008 →matched message body writer: java.lang.String@f62, "application/json" -> com.sun.jersey.core.impl.provider.entity.StringProvider@1c5ddffa 

这是(不起眼的)servlet配置:

  jersey com.sun.jersey.spi.container.servlet.ServletContainer  com.sun.jersey.config.property.packages com.application.my.jersey   com.sun.jersey.spi.container.ResourceFilters com.application.my.jersey.MyFilterFactory   com.sun.jersey.config.feature.Trace true  1  

这是示例资源:

 @Path("/post-stuff") @Produces(MediaType.APPLICATION_JSON) public final class TestResource { @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response execute( @FormParam("form_param_1") final String formParam1, @FormParam("form_param_2") final String formParam2 ) { return Response.created(URI.create("/")).entity("{}").build(); } } 

我正在使用Jersey 1.17。


对于那些感兴趣的人,我正在尝试滚动我自己的必需参数validation,如JERSEY-351中所述 。 我的解决方案适用于查询,cookie和标题参数 – forms参数对我很有帮助。

这是一个棘手的问题。 我删除了其他Jerseyfilter以消除问题,但错过了隐藏在web.xml底部的普通servletfilter:

  myFilter com.application.my.MyFilter   myFilter /*  

删除此filter修复了问题 – forms参数显示在泽西filter中。 但为什么? 我深入挖掘,将问题缩小到MyFilter的单个语句:

 request.getParameter("some_param") 

我试图通过删除MyFilter并在Jerseyfilter中进行相同的调用(通过注入HttpServletRequest )来更简化问题 – 但表单参数仍然显示出来。 当在传递给javax.servlet.Filter.doFilterorg.apache.catalina.connector.RequestFacade实例上调用getParameter时,问题似乎发生了。 这实际上是Tomcat的错误吗?

ServletRequest.getParameter的文档说:

如果参数数据是在请求主体中发送的,例如HTTP POST请求,则直接通过getInputStream()getReader()读取主体可能会干扰此方法的执行。

也许反过来也是如此 – 调用getParameter可能会被允许干扰实体输入流? 我不清楚方法的契约是否允许这种行为,以及它是否表示Tomcat,Jersey或其中的错误。

无论如何,那个旧的filter实际上并不需要,所以我的问题解决了,但只是删除它。


这是问题的完整再现(Tomcat 7.0):

web.xml

   test  jersey com.sun.jersey.spi.container.servlet.ServletContainer  com.sun.jersey.config.property.packages com.application.my   com.sun.jersey.spi.container.ResourceFilters com.application.my.TestFilterFactory   com.sun.jersey.config.feature.Trace false  1   jersey /*   servletFilter com.application.my.TestServletFilter   servletFilter /*   

TestServletFilter.java

 package com.application.my; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public final class TestServletFilter implements Filter { @Override public void init(FilterConfig config) { } @Override public void doFilter( final ServletRequest request, final ServletResponse response, final FilterChain chain ) throws IOException, ServletException { System.out.println("calling getParameter on " + request.getClass().getName()); request.getParameter("blah"); chain.doFilter(request, response); } @Override public void destroy() { } } 

TestFilterFactory.java

 package com.application.my; import java.util.Collections; import java.util.List; import com.sun.jersey.api.model.AbstractMethod; import com.sun.jersey.spi.container.ContainerRequest; import com.sun.jersey.spi.container.ContainerRequestFilter; import com.sun.jersey.spi.container.ContainerResponseFilter; import com.sun.jersey.spi.container.ResourceFilter; import com.sun.jersey.spi.container.ResourceFilterFactory; public final class TestFilterFactory implements ResourceFilterFactory { @Override public List create(final AbstractMethod method) { return Collections.singletonList(new ResourceFilter() { @Override public ContainerRequestFilter getRequestFilter() { return new ContainerRequestFilter() { @Override public ContainerRequest filter(final ContainerRequest request) { System.out.println("form: " + request.getFormParameters()); return request; } }; } @Override public ContainerResponseFilter getResponseFilter() { return null; } }); } } 

TestResource.java

 package com.application.my; import java.net.URI; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @Path("/post-stuff") @Produces(MediaType.APPLICATION_JSON) public final class TestResource { @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response execute( @FormParam("form_param_1") final String formParam1, @FormParam("form_param_2") final String formParam2 ) { System.out.println("form param_1: " + formParam1); System.out.println("form param_2: " + formParam2); return Response.created(URI.create("/")).entity("{}").build(); } } 

确保您的ResourceFilterTestResource#execute方法创建一个ResourceFilterFactory实例,然后创建一个ContainerRequestFilter实例:

 public class MyFilterFactory implements ResourceFilterFactory { @Override public List create(final AbstractMethod am) { return new ArrayList() {{ add(new ResourceFilter() { @Override public ContainerRequestFilter getRequestFilter() { return new ContainerRequestFilter() { @Override public ContainerRequest filter(final ContainerRequest request) { System.out.println(request.getFormParameters()); return request; } }; } @Override public ContainerResponseFilter getResponseFilter() { return null; } }); }}; } } 

从您提供的trace我不确定您的ContainerRequestFilter是否被调用。 应该有一个跟踪头包含这样的东西:

 →matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@b98df1f 

我测试的全部痕迹:

 HTTP/1.1 201 Created Location: http://localhost:8080/helloworld-webapp/helloworld/ Content-Type: text/plain X-Jersey-Trace-000: accept root resource classes: "/helloworld" X-Jersey-Trace-001: match path "/helloworld" -> "/application\.wadl(/.*)?", "/helloworld(/.*)?" X-Jersey-Trace-002: accept right hand path java.util.regex.Matcher[pattern=/helloworld(/.*)? region=0,11 lastmatch=/helloworld]: "/helloworld" -> "/helloworld" : "" X-Jersey-Trace-003: accept resource: "helloworld" -> @Path("/helloworld") com.sun.jersey.samples.helloworld.resources.HelloWorldResource@7449df0f X-Jersey-Trace-004: match path "" -> "" X-Jersey-Trace-005: accept resource methods: "helloworld", POST -> com.sun.jersey.samples.helloworld.resources.HelloWorldResource@7449df0f X-Jersey-Trace-006: matched resource method: public javax.ws.rs.core.Response com.sun.jersey.samples.helloworld.resources.HelloWorldResource.execute(java.lang.String,java.lang.String) X-Jersey-Trace-007: matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@6bc1b916 X-Jersey-Trace-008: matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@6bc1b916 X-Jersey-Trace-009: matched message body writer: java.lang.String@f62, "text/plain" -> com.sun.jersey.core.impl.provider.entity.StringProvider@4aae6c4e Transfer-Encoding: chunked Server: Jetty(6.1.24) 

编辑1:

启用请求LoggingFilter

  com.sun.jersey.spi.container.ContainerRequestFilters com.sun.jersey.api.container.filter.LoggingFilter  

编辑2:

还要确保之前没有其他Servlet或Jerseyfilter读取过InputStream 。 在这种情况下,实体输入流可能不再可用(但您仍然可以将@FormParam注入到资源方法中 – 如本例所示)。