How to get form parameters in query filters

I am trying to get the request form parameters in the request filter:

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

However, the form always seems empty. The HttpRequestContext.getFormParameters() documentation says:

Get the parameters of the form of the request object.

This method ensures that the request object is buffered so that it can be used by the application.

Return: form parameters, if there is a request object, and the content type is "application / x-www-form-urlencoded", otherwise an instance without parameters is returned.

My resource is annotated with @Consumes("application/x-www-form-urlencoded") , although it will not match until the request filter causes it to not work?

I tried to do some research, but could not find convincing evidence of whether this was possible. This is a 4 year discussion in which Paul Sandoz says:

If you work in Jersey filters or using the HttpRequestContext , you can get the form parameters as follows: [broken link to Jersey 1.1.1 HttpRequestContext.getFormParameters ]

I also found this 3-year discussion on how to get multipart / form-data form fields in a query filter. In it, Paul Sandoz uses the following code:

 // 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); 

I tried to imitate this approach instead of Form , but the result of request.getEntityInputStream() always an empty stream. And looking at the source of getFormParameters , this method already does the same:

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

I can’t understand what obscures the entity’s input stream before I get to it. Something in Jersey should consume it, because form parameters are later passed to the resource method. What am I doing wrong here, or is it impossible (and why)?

EDIT: Here is an example of a submitted request:

 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 

Here (somewhat redundant) register requests :

 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 > 

Here are the response headers for this request, including 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 

Here is the (invisible) servlet configurator:

 <servlet> <servlet-name>jersey</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>com.application.my.jersey</param-value> </init-param> <init-param> <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name> <param-value>com.application.my.jersey.MyFilterFactory</param-value> </init-param> <init-param> <param-name>com.sun.jersey.config.feature.Trace</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> 

Here is an example resource:

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

I am using Jersey 1.17.


For those who are interested, I am trying to collapse my own necessary parameter check, as described in JERSEY-351 . My solution here worked on requests, cookies, and params parameters - the forms are pulling me.

+8
source share
2 answers

It was complicated. I removed the other Jersey filters to fix them from the problem, but skipped the simple servlet filter hiding at the bottom of web.xml :

 <filter> <filter-name>myFilter</filter-name> <filter-class>com.application.my.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 

Removing this filter fixed the question form parameters that appeared in the Jersey filter. But why? I went deeper, narrowing the problem down to one statement in MyFilter :

 request.getParameter("some_param") 

I tried to further simplify the problem by deleting MyFilter and making the same call in the Jersey filter (by entering HttpServletRequest ), but the form parameters still appeared. The problem probably occurs when calling getParameter instance of org.apache.catalina.connector.RequestFacade , which is passed to javax.servlet.Filter.doFilter . So is this really a Tomcat bug?

The documentation for ServletRequest.getParameter reads:

If the parameter data was sent to the request body, for example, with an HTTP POST request, then reading the body directly through getInputStream() or getReader() can interfere with the execution of this method.

So, perhaps the opposite is also true - can an intervention in the entity input stream be allowed to call the getParameter function? It is not clear to me whether this contract allows a method of this behavior, and whether it indicates an error in Tomcat, Jersey or not.

In any case, this old filter is not really needed, so my problem is solved, but just deleted.


Here's the full reproduction of the problem (Tomcat 7.0):

web.xml :

 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>test</display-name> <servlet> <servlet-name>jersey</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>com.application.my</param-value> </init-param> <init-param> <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name> <param-value>com.application.my.TestFilterFactory</param-value> </init-param> <init-param> <param-name>com.sun.jersey.config.feature.Trace</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>jersey</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <filter> <filter-name>servletFilter</filter-name> <filter-class>com.application.my.TestServletFilter</filter-class> </filter> <filter-mapping> <filter-name>servletFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> 

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<ResourceFilter> create(final AbstractMethod method) { return Collections.<ResourceFilter>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(); } } 
+2
source share

Make sure your ResourceFilterFactory creates an instance of ResourceFilter for the TestResource#execute method, which then creates an instance of ContainerRequestFilter :

 public class MyFilterFactory implements ResourceFilterFactory { @Override public List<ResourceFilter> create(final AbstractMethod am) { return new ArrayList<ResourceFilter>() {{ 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; } }); }}; } } 

From the trace you provided, I'm not sure if your ContainerRequestFilter called. There should be another trace header containing something like this:

 →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 

All trace from my test:

 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) 

EDIT 1:

Enable LoggingFilter request:

 <init-param> <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name> <param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value> </init-param> 

EDIT 2:

Also make sure that no other servlet or Jersey filter has read the InputStream before. In this case, the entity input stream may not be available (but you can still add @FormParam to your resource method - as in this case).

+1
source share

All Articles