Visit Sponsor

Written by 12:03 pm Springboot

Support JSONP in Spring MVC – ResponseBody & ResponseEntity Guide

Cross-domain AJAX requests are restricted in browsers for security reasons. JSONP (JSON with Padding) remains a simple technique to support cross-domain JSON data retrieval by wrapping JSON in a JavaScript callback function.

Although modern applications increasingly use CORS (Cross-Origin Resource Sharing), JSONP is still relevant when interacting with legacy systems or environments where CORS isn’t fully supported.

In this guide, you’ll learn:

  • What JSONP is and when it’s appropriate
  • How to implement JSONP in Spring MVC using @ResponseBody and ResponseEntity
  • Security considerations and best practices

What Is JSONP and Why Use It?

JSONP is a pattern that allows browsers to load data from external origins by exploiting <script> tag behavior. Unlike regular AJAX, which is blocked by same-origin policy, script tags are allowed to load from any domain.

Instead of returning plain JSON:

{ "name": "John Doe" }

JSONP returns:

callback({ "name": "John Doe" });

Where callback is the function name supplied by the client. This allows the browser to execute the remote data as a script.

JSONP vs CORS (Modern Recommendation)

Before implementing JSONP, ask:

  • Can I enable CORS instead? CORS is the current standard.
  • Do I control both server and client?

When you can use CORS:

@CrossOrigin(origins = "https://example.com")
@GetMapping("/users")
public List<User> getUsers() { ... }

JSONP is an alternative when:

  • CORS cannot be configured
  • Interacting with JSONP-based legacy clients

Adding JSONP Support with @ResponseBody

Spring MVC naturally serializes return values from controller methods annotated with @ResponseBody. To support JSONP, we wrap the JSON inside the callback specified by the client.

Controller Example

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/jsonp")
    public String getUsersJsonp(
            @RequestParam(value = "callback", required = false) String callback) {

        List<User> users = getUsers(); // your service call

        // Serialize your user list into JSON
        ObjectMapper mapper = new ObjectMapper();
        String json = "";
        try {
            json = mapper.writeValueAsString(users);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Error serializing JSON", e);
        }

        if (callback != null) {
            return callback + "(" + json + ");";
        }

        return json;
    }
}

Explanation:

  • If a callback parameter is present, the response is wrapped in the function call.
  • If callback is absent, it returns plain JSON — useful for standard AJAX.

Note: This example manually serializes JSON using Jackson’s ObjectMapper for explicit control.

Supporting JSONP with ResponseEntity

You can achieve the same result while controlling HTTP headers, status codes, and content types.

@GetMapping("/jsonp-entity")
public ResponseEntity<String> getUsersJsonpResponse(
        @RequestParam(value = "callback", required = false) String callback) {

    List<User> users = userService.findAll();

    String json = "";
    try {
        json = new ObjectMapper().writeValueAsString(users);
    } catch (JsonProcessingException e) {
        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("Error processing JSON");
    }

    String output = (callback != null) ? callback + "(" + json + ");" : json;

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

    return new ResponseEntity<>(output, headers, HttpStatus.OK);
}

Benefits of ResponseEntity:

  • You control status codes
  • You set headers explicitly (e.g., caching, content type)

Ensuring Valid JSONP for Clients

Only Allow Safe Callback Names

For security, validate callback names to prevent script injection:

if (!callback.matches("[a-zA-Z0-9_\\.]+")) {
    return ResponseEntity.badRequest().body("Invalid callback parameter");
}

This prevents malicious input such as:

callback=evilFunction();alert('xss')

Set Content Type

While browsers treat JSONP as script, you should still control the content type:

headers.setContentType(MediaType.TEXT_PLAIN);  // or application/javascript

Using application/javascript is more correct for JSONP and aligns with modern Content-Type expectations.

Testing JSONP Endpoints (Client-Side Example)

JavaScript Example

<script>
    function handleResponse(data) {
        console.log("Received data:", data);
    }

    var script = document.createElement('script');
    script.src = "https://api.example.com/api/users/jsonp?callback=handleResponse";
    document.head.appendChild(script);
</script>

How It Works:

  1. Browser loads the script tag from another domain
  2. The server returns handleResponse(...)
  3. The client executes it immediately

This is the essence of JSONP.

When Not to Use JSONP

✔ If you control both ends — prefer CORS
✔ If you need POST/PUT/DELETE — JSONP only works with GET
✔ If you need secure headers or tokens — JSONP can’t send X-Auth headers

Use modern API architectures like REST with CORS + tokens instead.

Best Practices (Senior Engineering Insight)

From extensive API and backend experience:

  • Prefer CORS over JSONP for new services
  • Validate the callback parameter to prevent injection
  • Keep JSONP limited to read-only GET endpoints
  • Use ResponseEntity for better HTTP control
  • Document your API so consumers know whether JSONP is supported

These patterns maintain security and interoperability.

Summary

JSONP provides a lightweight way to serve cross-domain JSON using callback-wrapped responses. While modern development favors CORS, JSONP remains useful in legacy scenarios or with older clients that lack CORS support.

In Spring MVC, you can easily wrap responses in callbacks using:

  • @ResponseBody for simple padding
  • ResponseEntity<String> for full HTTP control

Add validation, headers, and clear documentation for safe, maintainable API endpoints.

Visited 7 times, 1 visit(s) today
Close