我在Grails中实现RESTful API,并使用自定义身份验证方案,该方案涉及签署请求正文(以类似于Amazon的S3身份验证方案的方式).因此,要验证请求,我需要访问原始POST或PUT正文内容来计算和验证数字签名.
我正在控制器中的beforeInterceptor中进行身份验证.所以我希望在拦截器中可以访问像request.body这样的东西,并且仍然能够在实际操作中使用request.JSON.我担心如果我使用getInputStream或getReader(ServletRequest提供的方法)在拦截器中读取正文,当我尝试通过request.JSON访问它时,正文将在操作中显示为空.
我正在从Django迁移到Grails,一年前我在Django中遇到了完全相同的问题,但它很快被修补了.Django提供了一个request.raw_post_data属性,您可以将其用于此目的.
最后,为了好看和RESTful,我希望这适用于POST和PUT请求.
任何建议或指示将不胜感激.如果它不存在,我更倾向于指出如何针对快速和肮脏的黑客实现优雅的解决方案.=)在Django中,我编辑了一些中间件请求处理程序,以便为请求添加一些属性.我是Groovy和Grails的新手,所以我不知道代码在哪里,但我不介意在必要时做同样的事情.
可以通过覆盖Servlet过滤器中的HttpServletRequest来实现.
您需要实现一个存储请求体的HttpServletRequestWrapper: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"); } } }
一个覆盖当前servletRequest的Servlet过滤器: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 SetMULTI_READ_HTTP_METHODS = new TreeSet (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() { } }
然后你需要grails install-templates
在src/templates/war中运行和编辑web.xml,并在charEncodingFilter定义之后添加它:
multireadFilter grails.util.http.MultiReadServletFilter multireadFilter /*
然后,您应该可以request.inputStream
根据需要随时拨打电话.
我没有测试过这个具体的代码/程序,但我过去做过类似的事情,所以它应该工作;-)
注意:请注意,巨大的请求会导致您的应用程序被终止(OutOfMemory ...)
从这里可以看出
http://jira.codehaus.org/browse/GRAILS-2017
只是关闭grails自动处理XML使得文本可以在控制器中访问.像这样
class EventsController { static allowedMethods = [add:'POST'] def add = { log.info("Got request " + request.reader.text) render "OK" }}
最好,安德斯