users@jersey.java.net

Re: Inconsistent behavior with POST

From: Tim McCune <tim_at_qless.com>
Date: Thu, 20 Mar 2008 10:45:21 -0700

Thanks again Paul. I'll try it out.

--Tim

On Thu, 2008-03-20 at 18:40 +0100, Paul Sandoz wrote:
> 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.
> >>
>