Search code examples
springspring-bootjsonp

How do I create a JSONP filter with /**/ prepended to the callback?


I am upgrading a Spring Boot application to version 2.0 and Spring Framework to version 5.1.

The application currently uses Spring's built in JSONP support using AbstractJsonpResponseBodyAdvice.

@ControllerAdvice
public class JsonpControllerAdvice extends AbstractJsonpResponseBodyAdvice {

    public JsonpControllerAdvice() {
        super("jsonp");
    }
}

However, JSONP support was deprecated in version 5.0.7 and removed in version 5.1 RC1. In addition, it's not feasible to switch to CORS at this time.

A final caveat is that the JavaScript callback method must begin with /**/. For example (truncated):

/**/jQuery1720351297557893959_1567180700293(...)

I've tried using jsonp-filter but I am unable to configure the callback to include /**/.

How do I create a custom Spring Boot JSONP filter with /**/ prepended to the callback?


Note: My example is similar to Spring Boot: Remove /**/ before JSONP callback function name. But I can't remove the /**/ because the existing frontend code expects it in the callback.


Solution

  • While you can't use jsonp-filter, you can define a simple filter based on it. For example:

    @Component
    public class JsonPFilter implements Filter {
        @Override public void doFilter(ServletRequest request, ServletResponse response,
                                       FilterChain chain) throws IOException, ServletException {
            String callback = null;
    
            if (request instanceof HttpServletRequest) {
                HttpServletRequest httpServletRequest = (HttpServletRequest) request;
                callback = httpServletRequest.getParameter("jsonp");
            }
    
            if (callback != null) {
                OutputStream out = response.getOutputStream();
                out.write(String.format("/**/%s(", callback).getBytes());
                chain.doFilter(request, response);
                out.write(new JsonPResponseWrapper((HttpServletResponse) response).getData());
                out.write(")".getBytes());
                out.close();
            } else {
                chain.doFilter(request, response);
            }
        }
    
        private static class JsonPResponseWrapper extends HttpServletResponseWrapper {
            private JsonPResponseWrapper(HttpServletResponse response) {
                super(response);
            }
    
            private byte[] getData() {
                return new ByteArrayOutputStream().toByteArray();
            }
        }
    }