Access to the source body of a PUT or POST request

I implement the RESTful API in Grails and use a special authentication scheme that includes signing the request body (similar to the Amazon S3 authentication scheme). Therefore, to authenticate the request, I need to access the contents of the original POST or PUT to calculate and verify the digital signature.

I am doing authentication in beforeInterceptor in the controller. So I want something like request.body to be available in the interceptor and still be able to use request.JSON in the actual action. I'm afraid if I read the body in the interceptor using getInputStream or getReader (methods provided by ServletRequest), the body will be empty in action when I try to access it through request.JSON.

I am moving from Django to Grails, and I had the same issue in Django a year ago, but it was quickly fixed. Django provides a request.raw_post_data attribute that you can use for this purpose.

Finally, to be good and RESTful, I would like this to work for POST and PUT requests.

Any tips or pointers are welcome. If this does not exist, I would prefer pointers to how to implement an elegant idea solution for fast and dirty hacks. =) In Django, I edited some middleware request handlers to add some properties to the request. I am very new to Groovy and Grails, so I have no idea where this code lives, but I would not mind doing the same if necessary.

+26
servlets grails
Jun 25 '09 at 23:36
source share
3 answers

This is possible by overriding the HttpServletRequest in the servlet filter.

You need to implement an HttpServletRequestWrapper that stores the request body: SRC / Java / Grails / Util / HTTP / MultiReadHttpServletRequest.java

package grails.util.http; import org.apache.commons.io.IOUtils; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletRequest; import javax.servlet.ServletInputStream; import java.io.*; import java.util.concurrent.atomic.AtomicBoolean; public class MultiReadHttpServletRequest extends HttpServletRequestWrapper { private byte[] body; public MultiReadHttpServletRequest(HttpServletRequest httpServletRequest) { super(httpServletRequest); // Read the request body and save it as a byte array InputStream is = super.getInputStream(); body = IOUtils.toByteArray(is); } @Override public ServletInputStream getInputStream() throws IOException { return new ServletInputStreamImpl(new ByteArrayInputStream(body)); } @Override public BufferedReader getReader() throws IOException { String enc = getCharacterEncoding(); if(enc == null) enc = "UTF-8"; return new BufferedReader(new InputStreamReader(getInputStream(), enc)); } private class ServletInputStreamImpl extends ServletInputStream { private InputStream is; public ServletInputStreamImpl(InputStream is) { this.is = is; } public int read() throws IOException { return is.read(); } public boolean markSupported() { return false; } public synchronized void mark(int i) { throw new RuntimeException(new IOException("mark/reset not supported")); } public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); } } } 

Servlet filter that overrides the current servletRequest: src / java / grails / util / http / MultiReadServletFilter.java

 package grails.util.http; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Set; import java.util.TreeSet; public class MultiReadServletFilter implements Filter { private static final Set<String> MULTI_READ_HTTP_METHODS = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) {{ // Enable Multi-Read for PUT and POST requests add("PUT"); add("POST"); }}; public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { if(servletRequest instanceof HttpServletRequest) { HttpServletRequest request = (HttpServletRequest) servletRequest; // Check wether the current request needs to be able to support the body to be read multiple times if(MULTI_READ_HTTP_METHODS.contains(request.getMethod())) { // Override current HttpServletRequest with custom implementation filterChain.doFilter(new MultiReadHttpServletRequest(request), servletResponse); return; } } filterChain.doFilter(servletRequest, servletResponse); } public void init(FilterConfig filterConfig) throws ServletException { } public void destroy() { } } 

Then you need to run grails install-templates and edit web.xml in src / templates / war and add this after the definition of charEncodingFilter:

 <filter> <filter-name>multireadFilter</filter-name> <filter-class>grails.util.http.MultiReadServletFilter</filter-class> </filter> <filter-mapping> <filter-name>multireadFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 

You can then call request.inputStream as often as you need.

I have not tested this specific code / procedure, but in the past I have done similar things, so it should work; -)

Note. Remember that huge requests can kill your application (OutOfMemory ...)

+39
Jun 26 '09 at 9:06
source share

As can be seen here

http://jira.codehaus.org/browse/GRAILS-2017

just turning off grails automatic XML processing makes text available in controllers. Like this

 class EventsController { static allowedMethods = [add:'POST'] def add = { log.info("Got request " + request.reader.text) render "OK" }} 

Best, Anders

+25
Oct 29 '09 at 17:49
source share

It seems that the only way to have access to the flow and request parameters for POST requests is to write a wrapper that overrides the read of the stream, as well as access to the parameter. Here is a great example:

Change body of HttpServletRequest

+1
Jun 22 2018-11-22T00:
source share



All Articles