Spring 5 JSONP deprecated. Use CORS

Until now say maybe you had a web service on a domain that returned some data, but you wanted to retrieve that data through ajax, from another domain. JSONP was a convenient approach but in Spring 5 we must use CORS since AbstractJsonpResponseBodyAdvice was deprecated.

How JSONP worked?

You can skip this section if you already know. Basically on your “other” domain where you wanted to issue an ajax call, you had a function that would take a parameter, the response you would like from the web service.

This was usually a javascript object and you would take it’s data to display it in the page or do other computations. Since calling HTTP to other domains from Ajax is blocked by the browser you made a method on the backend that took a method name and returned the object wrapped in that method name.

Say in your frontend you had a method like:

function sayHello(person) {
    alert('Hello ' + person.name);
}

And the backend had a service at https://www.superglobals.net/getName?id=4 that returned a json object:

{"name": "Catalin"}

Doing this was strictly forbidden.

jQuery.ajax("https://www.superglobals.net/getName?id=4").done(data => {sayHello(data);})

Instead you made a method on the backend that was like https://www.superglobals.net/getName?id=4&callback=sayHello which returned something like:

sayHello({"name": "Catalin"})

Basically the request was loaded by the browser as a regular javascript file (like the jQuery lib for example) and what it did was to call your function with the parameter received dynamically from the service.

But this is considered not safe and repeating the request meant loading the script again and so on. The replacement in Spring 5 of the JSONP method is…

Spring CORS

Let’s create a service that will display a banner. We want anyone on the internet be allowed to load images from us.

package net.superglobals.jsonp;

public class Banner {
	private String imgUrl;
	private String href;
	
	public String getImgUrl() {
		return imgUrl;
	}
	
	public void setImgUrl(String imgUrl) {
		this.imgUrl = imgUrl;
	}
	
	public String getHref() {
		return href;
	}
	
	public void setHref(String href) {
		this.href = href;
	}
	
}

Create a controller.

  • @RestController adds @ResponseBody by default.
  • @CrossOrigin does the magic, allowing any or specified domains to access this resource.
  • @GetMapping is another way of saying method = RequestMethod.GET
package net.superglobals.jsonp;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/banner")
public class Controller {
	@CrossOrigin
	@GetMapping("/show")
	public Banner getBanner() {
		Banner b = new Banner();
		b.setHref("https://wordpress.org");
		b.setImgUrl("https://s.w.org/style/images/about/wmark.png");
		return b;
	}
}

Main method is a simple Spring Boot 5 config, run and check the response to see that JSONP style wrapping is no longer needed.

package net.superglobals.jsonp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class JsonpApplication {

	public static void main(String[] args) {
		SpringApplication.run(JsonpApplication.class, args);
	}

}
{"imgUrl":"http://localhost:8080/jsonp/banner","href":"https://www.superglobals.net"} 

Now create a HTML file and place it on another server like Apache HTTP or other that runs on something else than 8080. Even though we type localhost if ports are different browser threats it as a different domain.

<html>
	<head>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
		<script>
			function showBanner(response) {
				var anch = document.createElement('a');
				anch.href = response.href;
				anch.target = '_blank';
				var img = document.createElement('img');
				img.src = response.imgUrl; 
				anch.appendChild(img); 
				var div = document.getElementById('banner');
				div.appendChild(anch);
			}
		</script>
		<script>
			$(document).ready(() => {
				jQuery.ajax("http://localhost:8080/banner/show").done(data => {showBanner(data);})
			});
		</script>
	</head>
	<body>
		<div id="banner">
			
		</div>
	</body>
</html>

With Spring 5 CORS we can call the method directly from anywhere and process the response directly, no need for JSONP anymore.

If you check you should have this result.

spring 5 jsonp

Read more about CORS here https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Check my AJAX example for more on Ajax.

FacebookTwitterLinkedin