1. 概述
如果你既开发后端也开发前端,那么您就知道浏览器在处理AJAX请求时所具有的同源策略约束,也就是我们常说的跨域问题。什么叫同源呢,如果两个页面的协议,端口(如果有指定)和主机都相同,则两个页面具有相同的源。否则,都是跨域。
当然,解决这种问题的方法有很多,其中一种(也是较常见的)方法是使用JSON-P,本文讨论Spring 对使用JSON-P数据格式的支持。
2. JSON-P in Action
很重要的一点是:浏览器不会对 <script>
标签施加同源策略,允许跨不同域加载脚本,即我们可以在 <script>
中加载不同源的资源的。JSON-P 技术实际上是利用这一点通过将JSON响应作为 javascript 函数的参数传递。
2.1 准备工作
在我们的示例中,我们使用一个简单的 Company 类:
1 | public class Company { |
并通过一个 Controller 的方法 返回Company实例的 JSON 数据:
1 |
|
在客户端,我们使用 jQuery 库来创建和发送AJAX请求:
1 | $.ajax({ |
如果我们页面是通过如下的 URL 来展示并进行AJAX请求:
1 | http://localhost:8080/spring-mvc-java/companyRest |
服务器的响应如下:
1 | {"id":1,"name":"Xpto"} |
由于请求是同源的(相同的协议、域和端口),因此响应不会被阻止,浏览器将允许JSON数据。
2.2 跨源请求
换种方式,如果我们通过如下的 URL 来访问页面呢:
1 | http://127.0.0.1:8080/spring-mvc-java/companyRest |
注意,我们的页面域名为 127.0.0.1 ,而在 js 代码中,AJAX 请求是请求的目标是 localhost 域,违反了同源策略。因此响应将被浏览器阻止,不会正常的返回想要的数据。
这个时候就需要使用JSON-P技术了,我们可以向请求添加回调参数:
1 | http://127.1.1.1:8080/spring-mvc-java/companyRest?callback=getCompanyData |
在 JS 中进行 AJAX 时请求添加以下参数:
1 | $.ajax({ |
getCompanyData 是指接收到响应时会调用的函数,同时服务端的响应也进行相应的包装,如下所示:
1 | getCompanyData({"id":1,"name":"Xpto"}); |
根据 JSONP 协议,这个时候浏览器不会阻止它,因为它会将响应视为在客户端和服务器之间协商并达成一致的脚本,因为请求和响应中都匹配 getCompanyData,这样就能解决跨域的问题。
3. 使用 AbstractJsonpResponseBodyAdvice 更改响应
从Spring 4.1开始,我们现在可以访问通过自定义一个 AbstractJsonpResponseBodyAdvice 的实现,来完成根据JSON-P标准的格式化响应。
其实非常的简单,我们自定义一个 AbstractJsonpResponseBodyAdvice 的实现,如下所示:
1 |
|
主要是传递给父类一个类似 密钥 的东西 callback,这个是在 JSON-P 返回时定义的回调方法,注意,这个需要和请求中的 jsonpCallback 参数名保持一致才能实现 JSON-P 的协议。
5. 验证
通过前面讨论的配置,我们可以使我们的REST应用程序响应JSON-P。在下面的示例中,我们将返回公司的数据,因此我们的AJAX请求URL应该是这样的:
1 | http://127.0.0.1:8080/spring-mvc-java/companyRest?callback=getCompanyData |
进行之前的 JSON-P 配置扣,响应将如下所示:
1 | `getCompanyData({"id":1,"name":"Xpto"});` |
如上所述,尽管源自不同的域,但此格式的响应不会被阻止。JsonpControllerAdvice 会被应用到所有 @ResponseBody和ResponseEntity 注解的方法上。