我在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的新手,所以我不知道代码在哪里,但我不介意在必要时做同样的事情。
答案 0 :(得分:42)
可以通过覆盖Servlet过滤器中的HttpServletRequest来实现。
您需要实现存储请求正文的HttpServletRequestWrapper: 的src /用于Java / Grail / 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 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() {
}
}
然后你需要运行grails install-templates
并编辑src / templates / war中的web.xml并在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>
然后,您应该可以根据需要随时拨打request.inputStream
。
我没有测试过这个具体的代码/程序,但我过去做过类似的事情,所以它应该有用; - )
注意:请注意,巨大的请求可能会终止您的应用程序(OutOfMemory ...)
答案 1 :(得分:26)
从这里可以看出
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"
}}
最佳, 安德斯
答案 2 :(得分:1)
似乎能够继续访问流和POST请求参数的唯一方法是编写一个覆盖流读取和参数访问的包装器。这是一个很好的例子: