【问题标题】:How enable JSONP in RESTEasy?如何在 RESTEasy 中启用 JSONP?
【发布时间】:2011-07-18 01:55:20
【问题描述】:

标题说明了我的问题。我需要将 DTO 包装到 javascript 方法回调中。目前我返回请求 JSON。但是在 Ajax 中使用它会出现问题,因为我将 GET 发送到其他域。当然还有安全警察。

我有想法创建附加服务。您有任何示例、链接或建议如何做到这一点。

【问题讨论】:

    标签: java json rest jsonp resteasy


    【解决方案1】:

    RESTEasy 中没有明确支持 JSONP,但是在您的应用程序中启用 JSONP 的一种简单方法是编写一个 Servlet 过滤器。

    这里有一些链接可以帮助您编写过滤器:

    当我有这个要求时,我最终写了自己的,因为我找到的例子似乎都没有一个很好的解决方案。以下是我对编写自己的过滤器的建议:

    • 仅在指定回调参数时包装响应(显然)

    • 仅在响应内容类型为 application/json 时包装响应(或者如果您想支持更广泛的变体选择,仅在响应内容类型为 application/jsonapplication/*+json 时包装)

    • 使用 HttpServletResponseWrapper 以便您可以调用转发链 (chain.doFilter),而无需将任何数据写入实际响应。转发链完成后,您可以检查内容类型,确保要将响应包装为 JSONP,然后将捕获的数据连同 JSONP 前缀和后缀一起写入 real 响应。

    • 当您决定将响应包装为 JSONP 时,请确保将响应内容类型更改为 text/javascript

    如果您之前没有对 Java EE 过滤器做过很多工作,您可能需要先阅读 Java EE 教程的相关部分:Filtering Requests and Responses

    【讨论】:

    • 我可以看看你的实现吗?我的一直在回调中返回 XML。
    • 恐怕你不能;我无法发布源代码:(您使用哪个 RESTEasy 提供程序来生成 JSON?我使用 resteasy-jackson-provider。确保您的资源方法标记为 @Produces("application/json") 然后在您的过滤器中, 检查已写入响应包装器的内容。如果前向链将 XML 写入响应包装器,则这与您的 JSONP 过滤器无关。尝试使用 Firebug 或 Chrome(带和不带回调参数)检查响应。如果您将代码发布在某个地方,我可以看看。
    • 嗨 joelittlejohn,你能帮帮我吗?我无法将内容类型设置为文本/javascript。 response.isCommitted() == true 并且结果不能改变类型。有什么解决办法吗?
    • @Tioma 我认为您将真正的响应传递给前向链,而不是使用响应包装器。如果您查看 Sergei 的答案并特别注意涉及 ByteArrayOutputStreamHttpServletResponseWrapper 的部分,那么这应该会有所帮助。
    【解决方案2】:

    我为这个问题制定了解决方案草案。试试吧。该解决方案通过 http get 参数获取数据并转换为虚拟 POST 请求。

    JQuery:

    function call(){
    var val = '{"routes":[{"arrivalAddress":{"fullAddress":"DME"},"destinationAddress":{"fullAddress":"SVO"}}],"carsCount":"1"}';
    var jHandler = "doMap";
    $.getJSON("http://xxx:yyy/app-0.0.0.1/rest/requestPrice?callback=" + jHandler + "&json=" + encodeURIComponent(val)+"&jsoncallback=?", null, null, "json");
    }
    
    function doMap(obj){
    alert(obj);
    }
    

    Service 接口中的声明

    @POST
    @Path("requestPrice")
    @Produces("application/json")
    @Consumes("application/json")
    PriceResponse requestPrice(PriceRequest request) throws ServiceException;
    

    过滤器类:

    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;
    import java.io.*;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    
    public class JSONPRequestFilter implements Filter {
        private String callbackParameter;
    
        public void doFilter(ServletRequest request, ServletResponse response,
                             FilterChain chain) throws IOException, ServletException {
            if (!(request instanceof HttpServletRequest)) {
                throw new ServletException("This filter can " +
                        " only process HttpServletRequest requests");
            }
    
            final HttpServletRequest httpRequest = (HttpServletRequest) request;
            final HttpServletResponse httpResponse = (HttpServletResponse) response;
    
            if (isJSONPRequest(httpRequest)) {
                RequestWrapper requestWrapper = new RequestWrapper(httpRequest);
                requestWrapper.setContentType("application/json; charset=UTF-8");
                requestWrapper.setHeader("cache-control", "no-cache");
                requestWrapper.setHeader("accept", "application/json");
                requestWrapper.setCharacterEncoding("UTF-8");
                requestWrapper.setBody(httpRequest.getParameter("json"));
                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
                HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(httpResponse) {
    
                    @Override
                    public ServletOutputStream getOutputStream() throws IOException {
                        return new ServletOutputStream() {
                            @Override
                            public void write(int b) throws IOException {
                                baos.write(b);
                            }
                        };
                    }
    
                    @Override
                    public PrintWriter getWriter() throws IOException {
                        return new PrintWriter(baos);
                    }
    
                    public String getData() {
                        return baos.toString();
                    }
                };
    
                chain.doFilter(requestWrapper, responseWrapper);
                response.getOutputStream().write((getCallbackParameter(httpRequest) + "(").getBytes());
                response.getOutputStream().write(baos.toByteArray());
                response.getOutputStream().write(");".getBytes());
    
                response.setContentType("text/javascript");
            } else {
                chain.doFilter(request, response);
            }
        }
    
        private String getCallbackMethod(HttpServletRequest httpRequest) {
            return httpRequest.getParameter(callbackParameter);
        }
    
        private boolean isJSONPRequest(HttpServletRequest httpRequest) {
            String callbackMethod = getCallbackMethod(httpRequest);
            return (callbackMethod != null && callbackMethod.length() > 0);
        }
    
        private String getCallbackParameter(HttpServletRequest request) {
            return request.getParameter(callbackParameter);
        }
    
        public void init(FilterConfig filterConfig) throws ServletException {
            callbackParameter = filterConfig.getInitParameter("callbackParameter");
        }
    
        public void destroy() {
        }
    
        void printRequest(HttpServletRequest request) throws IOException {
            {
                System.out.println("--------------Headers---------------");
                Enumeration en = request.getHeaderNames();
                while (en.hasMoreElements()) {
                    String val = en.nextElement().toString();
                    System.out.println(val + " :");
                    Enumeration en1 = request.getHeaders(val);
                    while (en1.hasMoreElements()) {
                        System.out.println("\t" + en1.nextElement());
                    }
                }
            }
            {
                System.out.println("------------Parameters--------------");
                Enumeration en = request.getParameterNames();
                while (en.hasMoreElements()) {
                    String val = en.nextElement().toString();
                    System.out.println(val + " :");
                    String[] en1 = request.getParameterValues(val);
                    for (String val1 : en1) {
                        System.out.println("\t" + val1);
                    }
                }
            }
            System.out.println("---------------BODY--------------");
            BufferedReader is = request.getReader();
            String line;
            while ((line = is.readLine()) != null) {
                System.out.println(line);
            }
            System.out.println("---------------------------------");
    
            System.out.println("ContentType: " + request.getContentType());
            System.out.println("ContentLength: " + request.getContentLength());
            System.out.println("characterEncodings: " + request.getCharacterEncoding());
            System.out.println("AuthType: " + request.getAuthType());
    
            System.out.println("ContextPath: " + request.getContextPath());
            System.out.println("Method: " + request.getMethod());
    
        }
    
        public static class RequestWrapper extends HttpServletRequestWrapper {
            Map<String, String> headers = new HashMap<String, String>();
    
            int contentLength;
            BufferedReader reader;
    
            public RequestWrapper(HttpServletRequest request) {
                super(request);
            }
    
            public void setHeader(String key, String value) {
                headers.put(key, value);
            }
    
            ByteArrayInputStream bais;
            public void setBody(String body) {
                bais = new ByteArrayInputStream(body.getBytes());
                contentLength = body.length();
                headers.put("content-length", Integer.toString(contentLength));
            }
    
            @Override
            public BufferedReader getReader() throws IOException {
                reader = new BufferedReader(new InputStreamReader(bais));
                return reader;
            }
    
            @Override
            public ServletInputStream getInputStream() throws IOException {
                return new ServletInputStream() {
                    @Override
                    public int read() throws IOException {
                        return bais.read();
                    }
                };
            }
    
            @Override
            public String getMethod() {
                return "POST";
            }
    
            private String contentType;
    
            public void setContentType(String contentType) {
                this.contentType = contentType;
                headers.put("content-type", contentType);
            }
    
            @Override
            public String getContentType() {
                return contentType;
            }
    
            @Override
            public int getContentLength() {
                return contentLength;
            }
    
            @Override
            public String getHeader(String name) {
                String val = headers.get(name);
                if (val != null) {
                    return val;
                }
                return super.getHeader(name);    //To change body of overridden methods use File | Settings | File Templates.
            }
    
            @Override
            public Enumeration getHeaders(final String name) {
                return super.getHeaders(name);
            }
    
            @Override
            public Enumeration getHeaderNames() {
                final Enumeration en1 = super.getHeaderNames();
                final Iterator it = headers.keySet().iterator();
                return new Enumeration() {
                    public boolean hasMoreElements() {
                        return en1.hasMoreElements() || it.hasNext();
                    }
    
                    public Object nextElement() {
                        return en1.hasMoreElements() ? en1.nextElement() : (it.hasNext() ? it.next() : null);
                    }
                };
            }
    
            @Override
            public int getIntHeader(String name) {
                String val = headers.get(name);
                if (val == null) {
                    return super.getIntHeader(name);
                } else {
                    return Integer.parseInt(val);
                }
            }
        }
    }
    

    web.xml

    <filter>
        <filter-name>JSONPRequestFilter</filter-name>
        <filter-class>xxxxx.JSONPRequestFilter</filter-class>
        <init-param>
            <param-name>callbackParameter</param-name>
            <param-value>callback</param-value>
        </init-param>
    </filter>
    
    <filter-mapping>
      <filter-name>JSONPRequestFilter</filter-name>
      <url-pattern>/rest/*</url-pattern>
    </filter-mapping>
    

    【讨论】:

      【解决方案3】:

      计划在 RESTEasy 2.3.6 Final/3.0-beta-4 (https://issues.jboss.org/browse/RESTEASY-342) 中发布支持 JSONP 的增强功能。我可以通过简单地复制their code from GitHub 来“向后移植”我使用 RESTEasy 2.3.5 的项目。

      RESTEasy 会根据注释自动选择新的提供者。一旦在 url 中看到名为“callback”的查询参数,它就会通过将结果包装在 js 回调中自动工作。这与 JQuery 向服务器发送的 JSONP 请求兼容。

      【讨论】:

        【解决方案4】:

        从@talawahdotnet 开始,我使用的是RestEasy 3.0.9.Final,并且支持JSONP,一旦启用,任何带有“回调”查询参数的请求都将被包装为JSONP。我正在使用 JBoss,所以其他容器的完整文档是 here。以下是我必须执行的步骤:

        1. 在你的 web.xml 添加:

          <context-param>
              <param-name>resteasy.providers</param-name>
              <param-value>org.jboss.resteasy.plugins.providers.jackson.JacksonJsonpInterceptor</param-value>
          </context-param>
          
        2. 确保您有一个 WEB-INF/jboss-deployment-structure.xml,其中包含:

          <jboss-deployment-structure>
              <deployment>
                  <dependencies>
                      <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import" annotations="true"/>
                  </dependencies>
              </deployment>
          </jboss-deployment-structure>
          
        3. 确保您的 pom.xml 中有一个 resteasy-jackson-provider 依赖项,类似于:

          <dependency>
              <groupId>org.jboss.resteasy</groupId>
              <artifactId>resteasy-jackson-provider</artifactId>
              <scope>provided</scope>
          </dependency>
          

        【讨论】:

        • 我正在输入回调参数并将其包装在响应中,但“类型”仍然显示 application/json。我是否需要在代码中注释 @GET 方法以以某种方式引用提供程序?
        • 老实说,我当时可能没有检查内容类型是否正确,但您可以尝试使用 @Produces 手动设置它或返回类型集的 Response 对象
        • 您可能会使用 application/json,我已经尝试过了,它运行良好...只是 RestEasy 版本 3.0.12 中的一个小更新:正确的拦截器是 org.jboss.resteasy.plugins.providers.jackson.Jackson2JsonpInterceptor,此外,在版本 3.0.10 上存在一个错误,当浏览器收到 json 响应时会导致 Javascript 错误:stackoverflow.com/a/29484700/3335350
        【解决方案5】:

        Resteasy claims to support JSONP 在 3.x 版本中开箱即用:

        如果您使用的是 Jackson,Resteasy 有 JSONP,您可以通过 添加提供者 org.jboss.resteasy.plugins.providers.jackson.JacksonJsonpInterceptor (Jackson2JsonpInterceptor 如果您使用的是 Jackson2 提供程序) 你的部署。如果响应的媒体类型是 json 和 给定回调查询参数,响应将是一个javascript sn -p 与回调定义的方法的方法调用 范围。例如:

        GET /resources/stuff?callback=processStuffResponse 将产生这个 回复:

        processStuffResponse() 这个支持默认 jQuery 的行为。

        您可以通过设置 callbackQueryParameter 属性。

        不过好像是因为RESTEASY-1168: Jackson2JsonpInterceptor does not render closing bracket而坏了

        所以 foo({"foo":"bar"} 被渲染而不是 foo({"foo":"bar"})

        这会导致“Uncaught SyntaxError: Unexpected Identifier”错误

        我已经提交了一个带有修复程序的pull-request,希望它可以进入下一个版本 3.0.12

        我知道这个问题很老了,但是当你搜索resteasy jsonp问题时它显示在Google的第一页,所以我决定更新它

        【讨论】:

          猜你喜欢
          • 2011-05-12
          • 2018-03-07
          • 2013-07-25
          • 2015-04-25
          • 2016-08-11
          • 2013-11-30
          • 2012-12-27
          • 1970-01-01
          • 2012-01-03
          相关资源
          最近更新 更多