当前位置:  开发笔记 > 编程语言 > 正文

访问PUT或POST请求的原始主体

如何解决《访问PUT或POST请求的原始主体》经验,为你挑选了2个好方法。

我在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的新手,所以我不知道代码在哪里,但我不介意在必要时做同样的事情.



1> Siegfried Pu..:

可以通过覆盖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 Set MULTI_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 ...)


这是有意义的,因为HttpServletRequestWrapper将getParameter()委托给原始请求的getParameter(),它只知道原始的ServletInputStream(在第一次调用包装器上的getInputStream()之后它将是空的).除了getParameter()及其朋友的手写覆盖之外,我似乎无法找到解决此问题的任何方法.奇怪的是没有人写博客或记录servlet过滤器似乎遇到过这种情况.除非我遗漏了什么......
它也不适合我.我在getInputStream和getReader都设置了一个断点,看来当request.getParameter读入正文时它没有被击中,因此参数数组永远不会被正确初始化.

2> anders.norga..:

从这里可以看出

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"
}}

最好,安德斯


所以为了澄清,我只是尝试了这个并且它有效,但是你不要导入grails.converters.*(这就是anders可能意味着'关闭grails自动处理XML')
推荐阅读
小妖694_807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有