Spring JSON-P with Jackson – 用Jackson的Spring JSON-P

最后修改: 2016年 8月 2日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

If you’ve been developing anything on the web, you’re aware of the same-origin policy constraint browsers have when dealing with AJAX requests. The simple overview of the constraint is that any request originating from a different domain, schema or port, will not be permitted.

如果你在网络上开发过任何东西,你就会知道浏览器在处理AJAX请求时的同源策略约束。该约束的简单概述是,任何来自不同域、模式或端口的请求都不被允许。

One way to relax this browser restriction when working with JSON data – is by using JSON with padding (JSON-P).

在处理JSON数据时,有一种方法可以放松这种浏览器的限制–就是使用带填充的JSON(JSON-P)。

This article discusses Spring’s support for working with JSON-P data – with the help of AbstractJsonpResponseBodyAdvice.

本文讨论了Spring对JSON-P数据工作的支持–借助于AbstractJsonpResponseBodyAdvice

2. JSON-P in Action

2.JSON-P的应用

The same-origin policy is not imposed over the <script> tag, allowing scripts to be loaded across different domains. JSON-P technique takes advantage of this by passing the JSON response as the argument of the javascript function.

同源策略没有强加在<script>标签上,允许脚本在不同的域中被加载。JSON-P技术通过将JSON响应作为javascript函数的参数来利用这一优势。

2.1. Preparation

2.1.准备工作

In our examples, we will use this simple Company class:

在我们的例子中,我们将使用这个简单的Company类。

public class Company {
 
    private long id;
    private String name;
 
    // standard setters and getters
}

This class will bind the request parameters and shall be returned from the server as JSON representation.

该类将绑定请求参数,并应从服务器返回为JSON表示。

The Controller method is a simple implementation as well – returning the Company instance:

控制器方法也是一个简单的实现–返回Company实例。

@RestController
public class CompanyController {

    @RequestMapping(value = "/companyRest",
      produces = MediaType.APPLICATION_JSON_VALUE)
    public Company getCompanyRest() {
        Company company = new Company(1, "Xpto");
        return company;
    }
}

On the client side we can use jQuery library to create and send an AJAX request:

在客户端,我们可以使用jQuery库来创建和发送一个AJAX请求。

$.ajax({
    url: 'http://localhost:8080/spring-mvc-java/companyRest',
    data: {
        format: 'json'
    },
    type: 'GET',
    ...
});

Consider an AJAX request against the following URL:

考虑针对以下URL的AJAX请求。

http://localhost:8080/spring-mvc-java/companyRest

The response from the server would be the following:

服务器的响应将是以下内容。

{"id":1,"name":"Xpto"}

As the request was sent against the same schema, domain, and port, the response will not get blocked, and JSON data will be allowed by the browser.

由于该请求是针对相同的模式、域和端口发送的,因此响应不会被阻止,JSON数据将被浏览器允许。

2.2. Cross-Origin Request

2.2.跨源请求

By changing the request URL to:

通过将请求的URL改为。

http://127.0.0.1:8080/spring-mvc-java/companyRest

the response will get blocked by the browser, due to request being sent from localhost to 127.0.0.1 which is considered a different domain and presents a violation of the same-origin policy.

响应将被浏览器阻止,因为请求从localhost发送到127.0.0.1,被认为是一个不同的域,违反了同源策略。

With JSON-P, we are able to add a callback parameter to the request:

通过JSON-P,我们能够在请求中添加一个回调参数。

http://127.1.1.1:8080/spring-mvc-java/companyRest?callback=getCompanyData

On the client side it’s as easy as adding the following parameters to the AJAX request:

在客户端,这就像在AJAX请求中添加以下参数一样简单。

$.ajax({
    ...
    jsonpCallback:'getCompanyData',
    dataType: 'jsonp',
    ...
});

The getCompanyData will be the function called when the response is received.

getCompanyData将是收到响应时调用的函数。

If the server formats the response like the following:

如果服务器对响应进行了如下格式化。

getCompanyData({"id":1,"name":"Xpto"});

browsers will not block it, as it will treat the response as a script negotiated and agreed upon between the client and the server on account of matching getCompanyData in both request and the response.

浏览器不会阻止它,因为它将把响应视为客户端和服务器之间协商和同意的脚本,因为在请求和响应中都匹配getCompanyData

3. @ControllerAdvice Annotation

3.@ControllerAdvice 注释

The beans annotated with @ControllerAdvice are able to assist all or a specific subset of Controllers and is used to encapsulate cross-cutting behavior shared between different Controllers. Typical usage patterns are related to exception handling, adding attributes to models or registering binders.

带有@ControllerAdvice注释的Bean能够帮助所有或特定的控制器子集,并被用来封装不同控制器之间共享的跨领域行为。典型的使用模式与异常处理向模型添加属性或注册绑定器有关。

Starting with Spring 4.1, @ControllerAdvice is able to register the implementations of ResponseBodyAdvice interface which allows changing the response after its being returned by a controller method but before being written by a suitable converter.

从Spring 4.1开始@ControllerAdvice能够注册ResponseBodyAdvice接口的实现,该接口允许在控制器方法返回后,在合适的转换器写入前改变响应。

4. Changing the Response Using AbstractJsonpResponseBodyAdvice

4.使用AbstractJsonpResponseBodyAdvice改变响应

Also starting with Spring 4.1, we now have access to the AbstractJsonpResponseBodyAdvice class – which formats the response according to JSON-P standards.

从Spring 4.1开始,我们现在可以访问AbstractJsonpResponseBodyAdvice类–它根据JSON-P标准格式化响应。

This section explains how to put the base class at play and change the response without making any changes to the existing Controllers.

本节解释了如何在不对现有控制器做任何改动的情况下,让基类发挥作用并改变响应。

In order to enable Spring support for JSON-P, let’s start with the configuration:

为了启用Spring对JSON-P的支持,让我们从配置开始。

@ControllerAdvice
public class JsonpControllerAdvice 
  extends AbstractJsonpResponseBodyAdvice {

    public JsonpControllerAdvice() {
        super("callback");
    }
}

The support is made using the AbstractJsonpResponseBodyAdvice class. The key passed on the super method is the one that will be used in URL requesting JSON-P data.

这种支持是使用AbstractJsonpResponseBodyAdvice类实现的。在超级方法上传递的键是将在请求JSON-P数据的URL中使用。

With this controller advice, we automatically convert the response to JSON-P.

有了这个控制器的建议,我们会自动将响应转换为JSON-P。

5. JSON-P With Spring in Practice

5.JSON-P与Spring的实践

With the previously discussed configuration in place, we are able to make our REST applications respond with JSON-P. In the following example, we will return company’s data, so our AJAX request URL should be something like this:

有了前面讨论的配置,我们就能让我们的REST应用程序用JSON-P来响应。在下面的例子中,我们将返回公司的数据,所以我们的AJAX请求URL应该是这样的。

http://127.0.0.1:8080/spring-mvc-java/companyRest?callback=getCompanyData

As a result of the previous configuration, the response will look as follows:

由于前面的配置,响应将看起来如下。

getCompanyData({"id":1,"name":"Xpto"});

As discussed, the response in this format will not get blocked despite originating from a different domain.

正如所讨论的,尽管来自不同的域,但这种格式的响应不会被阻止。

The JsonpControllerAdvice can easily be applied to any method that returns a response annotated with @ResponseBody and ResponseEntity.

JsonpControllerAdvice可以轻松地应用于任何返回带有@ResponseBodyResponseEntity注释的响应的方法。

There should be a function with the same name passed in the callback, getCompanyData, for handling all the responds.

在回调中应该有一个同名的函数,getCompanyData,用于处理所有的响应。

6. Conclusion

6.结论

This quick article shows how an otherwise tedious work of formatting the response to take advantage of JSON-P is simplified using the new functionality in Spring 4.1.

这篇文章展示了如何利用Spring 4.1中的新功能简化格式化响应的繁琐工作,以利用JSON-P的优势。

The implementation of the examples and code snippets can be found in this GitHub project.

可以在这个GitHub项目中找到实例和代码片断的实现。