1. What is the Same Origin Policy?
One of the current trends in web applications development is communicating with the content and functionality from two different sources. Companies like Google, Yahoo, and Amazon have allowed developers to implement their own applications, by opening up formerly proprietary APIs with simple one-line calls to their APIs.
This enables developers with new opportunities to integrate with third-party features such as maps, videos, and embed playlists, comments, likes, etc.
However, most modern browsers impose security restrictions that disallow the cross-domain web Service calls using Ajax. By default, browsers don’t support making cross-domain Ajax calls to prevent malicious use of resources.
If the JavaScript application attempts to call cross-origin resources, the browser will return the following error:
XMLHttpRequest cannot load URL. Origin http://yourwebserver.com is not allowed by Access-Control-Allow-Origin.</pre> Visit the below link for more details on browser security policy: <a href="http://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy" target="_blank" rel="noopener noreferrer">Same-origin policy.</a>
The solution to the above problem is JSONP.
2. What is JSONP?
JSONP (JSON with Padding) is a technique used in web development to overcome the cross-domain restrictions imposed by browsers to allow data to be retrieved from systems (third party) other than the one the page was served by.
3. Adding Support for JSONP in Spring
Since the Spring version 4.2 release, the JSONP is inbuilt into the Spring framework. In order to enable JSONP support for spring controllers, We need to declare an @ControllerAdvice
bean that
extends AbstractJsonpResponseBodyAdvice
.
Add the following class alongside your spring config to support JSONP requests.
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice; @ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { public JsonpAdvice() { super("callback"); } }
With such @ControllerAdvice
bean registered, it will be possible to request the JSON web service from another domain using a <script>
tag.
Let us consider the following post resource for our example:
public class Post { private int id; private String title; private String excerpt; private String body; }
4. Spring Controller
We do not have to make any changes to the REST controller methods. For the sake of simplicity and limited scope of this article, we’re skipping the full implementation of getPost()
method.
@RequestMapping(value = "/api/1.0/posts/{postId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity getPost(@PathVariable("postId") long postId) { return new ResponseEntity(postService.getPostById(postId), HttpStatus.OK); }
Now we can access the posts list resources via regular REST call or using JSON API call.
5. Testing Controllers
Accessing REST Resource:
$ curl -X GET http://localhost:8080/api/1.0/posts/1
Response: 200 OK
{ "id": 1, "title": "Hello World!!", "excerpt": "Lorem ipsum dolor sit amet", "description": "Lorem ipsum dolor sit amet, ..." }
Accessing REST Resource using JSONP:
$ curl -X GET http://localhost:8080/api/1.0/posts/1?callback=postCallback
Response: 200 OK
postCallback({ "id": 1, "title": "Hello World!!", "excerpt": "Lorem ipsum dolor sit amet", "description": "Lorem ipsum dolor sit amet, ..." })
Notice the difference in the results for both cases. When the resource is accessed using JSONP, the response is returned as JSONP payload as a JavaScript method.
If you want to access it from another domain, we can use it as
<script src="http://localhost:8080/api/1.0/posts/1?callback=postCallback" type="application/javascript"/>