Backend Development 9 min read

Master JSONP in Spring Boot: From Basics to Custom ResponseBodyAdvice

This article explains the JSONP technique, demonstrates how to implement it in Spring Boot with a simple REST endpoint, custom JSONP wrapper, ResponseBodyAdvice, and a customized HttpMessageConverter, and shows how to test the solution using HTML script tags.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master JSONP in Spring Boot: From Basics to Custom ResponseBodyAdvice

1. Introduction

JSONP (JSON with Padding) is an unofficial protocol that uses the <script> tag to bypass browser cross‑origin restrictions by returning a JavaScript function call containing JSON data. The client must define a callback function to receive the data. JSONP only supports GET requests and poses security risks such as XSS and CSRF, and has largely been replaced by safer solutions like CORS.

2. Practical Example

Assume an endpoint http://localhost:9100/jsonps returns the following JSON array:

<code>[{ "id":1, "name":"张三" },{ "id":2, "name":"李四" },{ "id":3, "name":"王五" }]</code>

To use JSONP, the client adds a callback query parameter, e.g. http://localhost:9100/jsonps?callback=getUsers . The server wraps the JSON in a call to the supplied function:

<code>getUsers([{ "id":1, "name":"张三" },{ "id":2, "name":"李四" },{ "id":3, "name":"王五" }]);</code>

When the page defines a getUsers function, the data is automatically processed.

3. Implementation in Spring Boot

3.1 Rest Controller

<code>@RestController
@RequestMapping("/jsonps")
public class JsonpController {
    static List<User> DATAS = List.of(
        new User(1L, "张三"),
        new User(2L, "李四"),
        new User(3L, "王五")
    );
    @GetMapping("")
    public List<User> queryUsers() {
        return DATAS;
    }
}</code>

The controller simply returns a list of User objects.

3.2 Custom JSONP Wrapper

<code>public class JsonpMappingJacksonValue extends MappingJacksonValue {
    private String jsonpFunction;
    public JsonpMappingJacksonValue(Object value) {
        super(value);
    }
    // getters and setters
}</code>

This class extends MappingJacksonValue and adds a jsonpFunction field that indicates whether the response should be transformed into JSONP.

3.3 Custom ResponseBodyAdvice

<code>@ControllerAdvice
public class JsonpControllerAdvice implements ResponseBodyAdvice<Object> {
    private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");
    private String jsonpQueryParamName = "callback";

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        JsonpMappingJacksonValue container = this.getOrCreateContainer(body);
        HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
        String value = servletRequest.getParameter(jsonpQueryParamName);
        if (value != null) {
            if (!CALLBACK_PARAM_PATTERN.matcher(value).matches()) {
                return container;
            }
            MediaType contentTypeToUse = new MediaType("application", "javascript", StandardCharsets.UTF_8);
            response.getHeaders().setContentType(contentTypeToUse);
            container.setJsonpFunction(value);
        }
        return container;
    }
    // ... helper methods omitted
}</code>

The advice wraps the original body in JsonpMappingJacksonValue and, when a valid callback parameter is present, sets the response content type to application/javascript and records the function name.

3.4 Overriding HttpMessageConverter

<code>@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter() {
        @Override
        protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
            String jsonpFunction = (object instanceof JsonpMappingJacksonValue)
                ? ((JsonpMappingJacksonValue) object).getJsonpFunction() : null;
            if (jsonpFunction != null) {
                generator.writeRaw("/**/");
                generator.writeRaw(jsonpFunction + "(");
            }
        }
        @Override
        protected void writeSuffix(JsonGenerator generator, Object object) throws IOException {
            String jsonpFunction = (object instanceof JsonpMappingJacksonValue)
                ? ((JsonpMappingJacksonValue) object).getJsonpFunction() : null;
            if (jsonpFunction != null) {
                generator.writeRaw(");");
            }
        }
    };
    return converter;
}</code>

The customized converter adds the JSONP function call prefix and suffix only when the wrapper contains a non‑null function name.

4. Testing the Solution

Without a callback parameter, the endpoint returns plain JSON. With callback=getUsers , the response is wrapped as shown earlier. An HTML page can load the data via a script tag:

<code>&lt;html&gt;
  &lt;head&gt;
    &lt;script&gt;
      function getUsers(users) {
        alert(JSON.stringify(users));
      }
    &lt;/script&gt;
    &lt;script src="http://localhost:9100/jsonps?callback=getUsers"&gt;&lt;/script&gt;
  &lt;/head&gt;
&lt;/html&gt;</code>

When opened in a browser, the alert displays the user list.

Spring BootCross-OriginJSONPResponseBodyAdviceHttpMessageConverter
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.