Implementing JSONP in Spring MVC 3.0.x
In Spring 3, it’s very easy to get a view to return JSON. However, there is no built-in mechanism to return JSONP. I was able to find a pretty good tutorial That uses Spring’s DelegatingFilterProxy. I implemented this solution and I got it to work, but I didn’t like the fact that I had to create a separate filter and a bunch of other classes just for that. I also wanted to use a specific extension (.jsonp) for JSONP just to make it more explicit. Spring uses MappingJacksonJsonView to return JSON. I figured that I could extend this view and have it return JSONP instead of JSON.
Although I pulled my hair out for an hour or two (I was getting a 404 when hitting my controller with with a .jsonp extension; I’ll explain why, later), the resulting solution is actually pretty simple. First, you need to extend MappingJacksonJsonView to create your own view. Since MappingJacksonJsonView already handles the creation of JSON, all we need to do is pad it with our callback. The way to do this is to override the MappingJacksonJsonView#render(Map
The next thing you need to do is include an entry for your new view in the springmvc-servlet.xml file. This part also took a little work. I wasn’t sure how to include my new view within the ContentNegotiatingViewResolver bean. But this post proved to be remarkably helpful. The post actually talks about returning XML or JSON (based on the extension), but I was able to adapt it to return JSON or JSON. Here’s what it looks like:
Finally, you need a url-pattern entry under servlet-mapping in your web.xml file so that the new .jsonp extension is recognized:
<servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.json</url-pattern> <url-pattern>*.jsonp</url-pattern> ... </servlet-mapping>
That’s pretty much it. With this code in place, you can hit your controller with http://my.domain.com/controllerName/actionName.jsonp?callback=myCallback and you’ll get your data back as JSONP! I thought this solution was pretty simple and a little less invasive than creating a filter. Comments/suggestions/criticisms for improvement are welcome!