java 如何在 RESTEasy 中启用 JSONP?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5350924/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 10:44:12  来源:igfitidea点击:

How enable JSONP in RESTEasy?

javajsonrestjsonpresteasy

提问by Tioma

Title say about my issue. I need wrap DTO in to a javascript method callback. Currently I return on request JSON. But problem with using this in Ajax because I send GET to other domain. and of course security police.

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

I have idea to create addition provide. Have you any example, links or suggestion how can do this.

我有创建附加提供的想法。你有任何例子,链接或建议如何做到这一点。

回答by joelittlejohn

There's no explicit support for JSONP in RESTEasy, however one easy way to enable JSONP in your application is to write a Servlet Filter.

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

Here's a few links that can help you write a filter:

以下是一些可以帮助您编写过滤器的链接:

When I had this requirement I ended up writing my own since none of the examples I found seemed to quite nail it. Here's my advice for writing your own filter:

当我有这个要求时,我最终写了自己的,因为我发现的例子似乎都没有完全说明它。这是我编写自己的过滤器的建议:

  • only wrap the response if a callback parameter is specified (obviously)

  • only wrap the response if the response content type is application/json(or if you want to support a wider selection of variants, only wrap if the response content type is application/jsonor application/*+json)

  • use an HttpServletResponseWrapper so that you can invoke the forward chain (chain.doFilter) without writing any data to the real response. Once the forward chain is complete you can then check the content type, make sure you want to wrap the response as JSONP, then write the captured data into the realresponse, along with the JSONP prefix and suffix.

  • when you do decide to wrap the response as JSONP, make sure you change the response content type to text/javascript

  • 如果指定了回调参数,则仅包装响应(显然)

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

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

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

If you haven't done much with Java EE Filters before, you may want to read the relevant section of the Java EE tutorial first: Filtering Requests and Responses.

如果您之前没有对 Java EE 过滤器做过多少工作,您可能需要先阅读 Java EE 教程的相关部分:过滤请求和响应

回答by Sergei Terehov

I make draft workaround for this problem. Try it. This solution takes data via http get parameters and translate to virtual POST request.

我为这个问题制定了解决方案草案。试试看。此解决方案通过 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);
}

Declaration in Service interface

Service接口中的声明

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

Filter class:

过滤器类:

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

网页.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>

回答by talawahtech

An enhancement to support JSONP is scheduled to be released in RESTEasy 2.3.6 Final/3.0-beta-4 (https://issues.jboss.org/browse/RESTEASY-342). I was able to "backport" it my project which uses RESTEasy 2.3.5 by simply copying their code from GitHub.

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

RESTEasy automatically picks up the new provider based on the annotation. It works automatically by wrapping your results in a js callback once it sees a query parameter named "callback" in the url. This is compatible with what JQuery sends to the server for JSONP requests.

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

回答by Andy

To follow on from @talawahdotnet, I'm using RestEasy 3.0.9.Final and there is support for JSONP, once enabled, any request with a "callback" query parameter will be wrapped as JSONP. I'm using JBoss so the full docs are herefor other containers. Here's the steps I had to do:

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

  1. In your web.xml add:

    <context-param>
        <param-name>resteasy.providers</param-name>
        <param-value>org.jboss.resteasy.plugins.providers.Hymanson.HymansonJsonpInterceptor</param-value>
    </context-param>
    
  2. Make sure you have a WEB-INF/jboss-deployment-structure.xml with:

    <jboss-deployment-structure>
        <deployment>
            <dependencies>
                <module name="org.jboss.resteasy.resteasy-Hymanson-provider" services="import" annotations="true"/>
            </dependencies>
        </deployment>
    </jboss-deployment-structure>
    
  3. Make sure you have a resteasy-Hymanson-provider dependency in your pom.xml, something like:

    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-Hymanson-provider</artifactId>
        <scope>provided</scope>
    </dependency>
    
  1. 在你的 web.xml 添加:

    <context-param>
        <param-name>resteasy.providers</param-name>
        <param-value>org.jboss.resteasy.plugins.providers.Hymanson.HymansonJsonpInterceptor</param-value>
    </context-param>
    
  2. 确保你有一个 WEB-INF/jboss-deployment-structure.xml :

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

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

回答by bedrin

Resteasy claims to support JSONPout of the box in 3.x version:

Resteasy声称在 3.x 版本中支持开箱即用的JSONP

If you're using Hymanson, Resteasy has JSONP that you can turn on by adding the provider org.jboss.resteasy.plugins.providers.Hymanson.HymansonJsonpInterceptor (Hymanson2JsonpInterceptor if you're using the Hymanson2 provider) to your deployments. If the media type of the response is json and a callback query parameter is given, the response will be a javascript snippet with a method call of the method defined by the callback parameter. For example:

GET /resources/stuff?callback=processStuffResponse will produce this response:

processStuffResponse() This supports the default behavior of jQuery.

You can change the name of the callback parameter by setting the callbackQueryParameter property.

如果您使用 Hymanson,Resteasy 具有 JSONP,您可以通过将提供程序 org.jboss.resteasy.plugins.providers.Hymanson.HymansonJsonpInterceptor(如果您使用 Hymanson2 提供程序,则为 Hymanson2JsonpInterceptor)添加到您的部署中来打开它。如果响应的媒体类型是 json 并且给出了回调查询参数,则响应将是一个 javascript 片段,其中包含回调参数定义的方法的方法调用。例如:

GET /resources/stuff?callback=processStuffResponse 将产生这个响应:

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

您可以通过设置 callbackQueryParameter 属性来更改回调参数的名称。

However, it seems that it is broken due to RESTEASY-1168: Hymanson2JsonpInterceptor does not render closing bracket

但是,由于RESTEASY-1168:Hymanson2JsonpInterceptor 不呈现右括号,它似乎已损坏

So foo({"foo":"bar"}is rendered instead of foo({"foo":"bar"})

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

And that causes "Uncaught SyntaxError: Unexpected Identifier" error

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

I have submitted a pull-requestwith a fix and hopefully it should get into next release 3.0.12

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

I know that this question is pretty old, but it is shown on the first page of Google when you search for resteasy jsonp problems, so I decided to update it

我知道这个问题很老了,但是搜索resteasy jsonp问题的时候google第一页就显示出来了,所以决定更新一下