users@jersey.java.net

Re: Inconsistent behavior with POST

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Thu, 20 Mar 2008 18:40:45 +0100

Tim McCune wrote:
> Thanks for the reply Paul, I appreciate it. That sounds about like what
> I expected. As a simple workaround right now, I stuck another filter in
> the chain that just calls request.getParameterMap() on POSTs, similar to
> your #2.
>

I have not tested it but the code below should work filling in the
blanks (as long as you use FormURLEncodedProvider with POST) and it
avoids the need to use an additional filter and expose servlet stuff to
the resource:

   @Provider
   @ConsumeMime("application/x-www-form-urlencoded")
   public class FormReader extends
       MessageBodyReader<FormURLEncodedProvider> {

      // Note that we have switched from @Resource to @Context
      // in the trunk
      @Context HttpServletRequest r;

      public boolean isReadable(Class<?> type) {
        return r != null && type == FormURLEncodedProperties.class
      }

      FormURLEncodedProvider readFrom(Class<T> type, MediaType mediaType,
             MultivaluedMap<String, String> httpHeaders,
             InputStream entityStream) throws IOException {
          FormURLEncodedProperties p = new FormURLEncodedProperties();
          Map sp = r.getParameterMap();
          // TODO:
          // Check if 'sp' is empty and there is available bytes on input
          // stream. This indicates not being used with POST method throw
          // exception or parse

          // Copy keys/values from 'sp' to 'p'
          ...
          return p;
      }
   }

This is something i am considering including with the Servlet
configuration to work around the side-affects of some valves/filters and
confusion it causes that you have observed. But i am not sure i want to
include it by default (perhaps enabled via an init-param) because of the
direct dependency on the HttpServletRequest (the reason being is that it
may be possible to reuse readers/writers in other contexts like for Java
typed multipart MIME processing and the above reader is not reusable in
such contexts).

Paul.

> Regards,
> Tim
>
> On Thu, 2008-03-20 at 11:52 +0100, Paul Sandoz wrote:
>> Hi Tim,
>>
>> Tim McCune wrote:
>>> I'll preface this with the fact that I'm still using version 0.4, so if
>>> a good answer is "already fixed in a newer version", then awesome.
>>>
>> I can reproduce it in the trunk.
>>
>>
>>> I'm running into problems using POST with my resources. I just want to
>>> get at application/x-www-form-urlencoded name/value pairs. If I pass a
>>> FormURLEncodedProperties parameter to my resource method, that seems to
>>> work, until I introduce a filter or a Tomcat valve (e.g. request dumper)
>>> somewhere in the processing chain before it, that reads from the input
>>> stream. Jersey doesn't seem to reset the stream (not sure if that's
>>> even possible), and so I end up with an empty FormURLEncodedProperties.
>>>
>> For the most part Jersey is container independent and tries to make as
>> minimal assumptions as possible on the container used to get the HTTP
>> meta-data and request entity. Which is why it always uses the input
>> stream for processing a request entity.
>>
>> The problem is the Tomcat Request Dumper Valve has side effects that
>> result in the processing of the input stream. In general if any
>> filter/valve has side effects that affect the input stream then it can
>> potentially break many types of servlet or another type of filter. For
>> example, if a logging filter was configured to occur after the Tomcat
>> Request Dumper Valve and that filter wants to dump the request entity as
>> bytes, or a filter that needs to verify the signature of the request entity.
>>
>>
>>> Another approach that I've tried is adding a
>>> @Resource private HttpServletRequest _request;
>>> field to my resource, and then just reading the request parameters
>>> directly from that inside my resource method. That one's a bit more
>>> interesting, as it seems to work exactly backwards of the other
>>> approach. :) Jersey looks to be wrapping the servlet request and
>>> causing some problems. If I don't touch the servlet request before it
>>> gets to Jersey, I get an empty set of parameters from the request.
>>> Then, to try to debug it, I added this line:
>>> System.out.println("------------JERSEY SERVLET: " +
>>> req.getParameterMap());
>>> to the beginning of the Jersey servlet's service() method, and after
>>> that I was always able to get my parameters from the request.
>>>
>>> I didn't dig too deeply into the code, but the overall problem seems to
>>> be around the way that Jersey wants to read the parameters directly from
>>> the servlet input stream, instead of reading them from the actual
>>> servlet request. It's making too many assumptions about the
>>> environment.
>>>
>> The reason is because Jersey reads the input stream and this means
>> ServletRequest.getParameter* will not function, from the JavaDoc:
>>
>> "If the parameter data was sent in the request body, such as occurs
>> with an HTTP POST request, then reading the body directly via
>> getInputStream() or getReader() can interfere with the execution of
>> this method."
>>
>> But of course if set up the valve it will work because the valve gets
>> the parameters.
>>
>>
>>> Is there a good recommended, reliable way to read posted,
>>> form-urlencoded parameters in a Jersey resource?
>>>
>> Two possible immediate solutions:
>>
>> 1) Use a filter/valve that does not have side-effects; or
>>
>> 2) Try the following if you need to use a filter/valve that has
>> side-effects:
>>
>> @Path("/") public class FormResource {
>> @Resource HttpServletRequest request;
>>
>> @POST public String formPost() {
>> return request.getParameterMap();
>> }
>> }
>>
>> I recommend the former if possible. The latter works because the
>> "formPost" method does not have any request entity parameter does does
>> not consume the input stream.
>>
>> I might be able to develop a special form processing message body reader
>> that is used with servlet that works around potential side-effects of
>> filters but i need to think more about it...
>>
>> Paul.
>>

-- 
| ? + ? = To question
----------------\
    Paul Sandoz
         x38109
+33-4-76188109