1. 概述

在Web开发中,你可能已经遇到过浏览器对于AJAX请求的同源策略限制。简单来说,如果请求来自不同的域名、协议或端口,浏览器会禁止这种请求。

一种缓解这种浏览器限制的方法是使用JSON-P(JSON with Padding)。本文将讨论Spring如何通过AbstractJsonpResponseBodyAdvice来处理JSON-P数据。

2. JSON-P实战

同源策略不会影响<script>标签,因此可以通过它跨域加载脚本。JSON-P技术利用这一点,将JSON响应作为javascript函数的参数传递。

2.1. 准备工作

以一个简单的Company类为例:

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

这个类用于绑定请求参数,并将以JSON形式返回服务器。

控制器方法也很简单,直接返回Company实例:

@RestController
public class CompanyController {

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

客户端可以使用jQuery库创建并发送AJAX请求:

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

假设我们向以下URL发起AJAX请求:

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

服务器的响应如下:

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

由于请求的协议、域名和端口相同,浏览器不会阻止响应,允许JSON数据的传输。

2.2. 跨域请求

若将请求URL改为:

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

由于请求从localhost发送到127.0.0.1,被视为不同的域名,违反了同源策略,浏览器会阻止响应。

通过JSON-P,我们可以在请求中添加callback参数:

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

客户端只需在AJAX请求中添加如下参数:

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

getCompanyData将在接收到响应时被调用。

如果服务器的响应格式如下:

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

由于匹配了请求中的getCompanyData,浏览器不会阻止,因为这被视为客户端与服务器之间协商好的脚本。

3. @ControllerAdvice注解

带有@ControllerAdvice注解的bean可以协助所有或部分控制器,并封装不同控制器共享的横切行为。常见用法包括异常处理、为模型添加属性或注册绑定器。

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

4. 使用AbstractJsonpResponseBodyAdvice改变响应

同样从Spring 4.1开始,我们可以使用AbstractJsonpResponseBodyAdvice类,它按照JSON-P标准格式化响应。

本节将解释如何使用基类,不修改现有控制器的情况下改变响应。

为了启用Spring对JSON-P的支持,首先配置如下:

@ControllerAdvice
public class JsonpControllerAdvice 
  extends AbstractJsonpResponseBodyAdvice {

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

使用AbstractJsonpResponseBodyAdvice类来提供支持。传递给超类的关键字将用于请求JSON-P数据的URL。

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

5. 实践中的Spring与JSON-P

在前面讨论的配置下,我们的REST应用可以响应JSON-P。例如,如果要返回公司的数据,AJAX请求的URL应为:

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

由于之前的配置,响应将如下所示:

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

如前所述,尽管来自不同的域名,这种格式的响应不会被阻止。

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

需要有一个名为getCompanyData的函数来处理所有响应。

6. 总结

这篇文章展示了Spring 4.1新功能如何简化原本繁琐的将响应格式化为JSON-P的过程。

示例的实现和代码片段可在此GitHub项目中找到。


« 上一篇: Dozer映射指南
» 下一篇: JMockit高级使用