开发者

How enable JSONP in RESTEasy?

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.

I have idea to create addition provide. Have you any example, links or s开发者_如何学Pythonuggestion how can do this.


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.

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

  • jsonp-java: server side filter wraps any response into a jsonp callback

  • Serving up JSONP from your JAX-RS Web Services

  • Implementing a Servlet Filter for JSONP callback with Spring’s DelegatingFilterProxy (if you're using Spring)

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/json or 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 real response, 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

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.


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

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

@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

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


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.

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.


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 here for other containers. Here's the steps I had to do:

  1. In your web.xml add:

    <context-param>
        <param-name>resteasy.providers</param-name>
        <param-value>org.jboss.resteasy.plugins.providers.jackson.JacksonJsonpInterceptor</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-jackson-provider" services="import" annotations="true"/>
            </dependencies>
        </deployment>
    </jboss-deployment-structure>
    
  3. Make sure you have a resteasy-jackson-provider dependency in your pom.xml, something like:

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


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

If you're using Jackson, Resteasy has JSONP that you can turn on by adding the provider org.jboss.resteasy.plugins.providers.jackson.JacksonJsonpInterceptor (Jackson2JsonpInterceptor if you're using the Jackson2 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.

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

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

And that causes "Uncaught SyntaxError: Unexpected Identifier" error

I have submitted a pull-request with a fix and hopefully it should get into next release 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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜