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
可以轻松应用于任何带有@ResponseBody
和ResponseEntity
注解返回响应的方法。
需要有一个名为getCompanyData
的函数来处理所有响应。
6. 总结
这篇文章展示了Spring 4.1新功能如何简化原本繁琐的将响应格式化为JSON-P的过程。
示例的实现和代码片段可在此GitHub项目中找到。