本文会代码层面对CORS问题进行剖析

CORS相关相关概念可参考http://www.cnblogs.com/softidea/p/5496719.html

ERROR info:

XMLHttpRequest cannot load http://localhost:8080/jsonp/scene1/user/1. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.

JSONP(Javascript Object Notation With Padding)需要客户端和服务器支持

JSONP解释
在解释JSONP之前,我们需要了解下”同源策略“这个概念,这对理解跨域有帮助。
基于安全的原因,浏览器是存在同源策略机制的,同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载额文档的属性。有点绕,说的简单点就是浏览器限制脚本只能和同协议、同域名、同端口的脚本进行交互。

什么是jsonp格式呢?
API原文:
如果获取的数据文件存放在远程服务器上(域名不同,也就是跨域获取数据),则需要使用jsonp类型。
使用这种类型的话,会创建一个查询字符串参数 callback=? ,这个参数会加在请求的URL后面。
服务器端应当在JSON数据前加上回调函数名,以便完成一个有效的JSONP请求。

意思就是远程服务端需要对返回的数据做下处理,根据客户端提交的callback的参数,返回一个callback(json)的数据,而客户端将会用script的方式处理返回数据,来对json数据做处理。JQuery.getJSON也同样支持jsonp的数据方式调用。

JSONP就是为了解决这一问题的,JSONP是英文JSON with Padding的缩写,是一个非官方的协议。他允许服务端生成script tags返回值客户端,通过JavaScript callback的形式来实现站点访问。JSONP是一种script tag的注入,将server返回的response添加到页面是实现特定功能。
简而言之,JSONP本身不是复杂的东西,就是通过scirpt标签对javascript文档的动态解析绕过了浏览器的同源策略。
JSONP原理及实现
接下来,来实际模拟一个跨域请求的解决方案。后端为spring MVC架构的,前端则通过Ajax进行跨域访问。
1、首先客户端需要注册一个callback(服务端通过该callback(jsonp)可以得到js函数名(jsonpCallback)),然后以JavaScript语
法的方式,生成一个function
2、接下来,将JSON数据直接以入参的方式,放置到function中,这样就生成了一段js语法文档,返回给客户端。
3、最后客户端浏览器动态的解析script标签,并执行返回的JavaScript语法文档片段,此时数据作为参数传入到了预先定义好的
回调函数里(动态执行回调函数)。
这种动态解析js文档和eval函数是类似的。

 

客户端的支持:

This is how I'm using JSONP with Spring MVC, just modify it according to your needs:
on Server Side:
下例中服务器端处理参数名为“callback"的客户端数据,此数据在客户端的JS代码必须存在,可以指定参数值,也不可以不指定,如果不指定,会由浏览器自动生成
类似下面这种格式:

http://localhost:8080/jsonp/scene1/user/1?callback=jQuery3210430263573155826_1502264239363&time=1502265440209&_=1502264239364
@RequestMapping(value="/notes/callback.json", method=RequestMethod.GET)
public void jsonpCallback(@RequestParam("callback") String callback, HttpServletResponse response) {
response.setContentType("text/javascript; charset=UTF-8");
PrintWriter out = response.getWriter();
out.print(callback + "(" + jsonDataString + ")");
}

On client side:

<script src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
<script type="text/javascript"> function yourfunction() {
jQuery.getJSON("http://localhost:8080/notes/callback.json?callback=?",
function(data) {
alert(data.someParam);
});
} </script>

指定callback参数对应的值:

$.ajax({
url: "http://tonghuashuo.github.io/test/jsonp.txt",
dataType: 'jsonp',
jsonp: "callback",
jsonpCallback: "dosomething"
})
.done(function(res) {
console.log("success");
console.log(res);
})
.fail(function(res) {
console.log("error");
console.log(res);
});

后端关键代码(以PHP为例)

$result   = "{'data':'JSONP Works'}"; // 这里省略了数据库查询等操作,直接给出返回值
$callback = $_GET['callback']; // 最好加上判空和默认值,以防拿不到参数
echo $callback."(".$result.")"; // 返回的结果
// dosomething({"data":"JSONP Works"});

将上述代码放到你本地localhost中,尝试运行一下,顺利的话应该会在浏览器的控制台中得到以下的内容:

> success
> Object {data: "JSONP Works"}

实际发送出来的完整请求长这样:http://tonghuashuo.github.io/test/jsonp.txt?callback=dosomething&_=1471419449018。,后面的随机字符串是jQuery加上的。

区别于常规的 AJAX 请求,这里真正需要关心的参数有以下 3 个

  • dataType: 'jsonp',用于表示这是一个 JSONP 请求。
  • jsonp: 'callback',用于告知服务器根据这个参数获取回调函数的名称,通常约定就叫 callback。
  • jsonpCallback: 'dosomething',指定我们自己的回调函数名称,也是前面callback参数的值。远程服务接受callback参数的值就不再是自动生成的回调名

其中jsonpCallback参数是可以省略的,jQuery 会自动生成一个随机字符串作为函数名,推荐这么做,以减少不必要的命名工作,同时排除潜在的安全隐患。这里由于Github Page没有后台服务,我们只能指定一个名字。注意:省略jsonpCallback的同时,jsonp参数必须指明,不能为false。

jQuery 还支持将jsonp设置为false以避免callback参数出现在请求 URL 中,但这需要前后端配合,前端必须要指定jsonpCallback的值为一个函数名,后端由于无法从请求中获取回调函数的名称,因此也必须固定使用同名的字符串进行拼接。

Spring 4 MVC + JSONP Example with REST, @ResponseBody and ResponseEntity
客户端代码示例:

$("#but1").click(function(){
$.ajax({
url:'http://127.0.0.1:8080/DevInfoWeb/get',
type: "get",
async: false,
dataType: "jsonp",
jsonp: "callbackparam", //服务端用于接收callback调用的function名的参数
jsonpCallback: "success_jsonpCallback", //callback的function名称,服务端会把名称和data一起传递回来
success: function(json) {
alert(json);
},
error: function(){alert('Error');}
});
});

<script language="JavaScript">
$(document).ready(function() {
$.ajax({
url:'http://localhost:8080/test/getPopularity',
dataType:'jsonp',
success:function(data){
console.log("返回Json:")
console.log(data)
}
});
});
</script>

http://localhost:8080/test/getPopularity?callback=jsonp

In this page we will provide Spring 4 MVC and JSONP example with REST, @ResponseBody and ResponseEntity. JSONP is JSON with padding. It supports JavaScript code running in web browser to request data from a server in different domain which is normally prohibited because of same-origin policy. According to this policy a web browser can allow the script code of one web browser to access data from another web browser within the same domain. Same-origin policy is because of web application security model. But this policy is not forced to <script> tag by web browser. From here the role of JSONP comes into picture. JSONP allows to access data from different domain using <script> tag by web browser. If we have a URL as
http://localhost:8080/concretepage-1/book1?callback=myfunction
and if it throws the JSONP response as

myfunction({"bookName":"Godan","writer":"Premchand"});

then in another domain, we can use it as

<script src=" http://localhost:8080/concretepage-1/book1?callback=myfunction " type="application/javascript"> </script>

In our client code to access JSONP, there should already be a function named myfunction() defined in script code.
To throw the JSONP response using spring, it provides AbstractJsonpResponseBodyAdvice class which is implemented by a class annotated with @ControllerAdvice.

Spring 4 Support for JSONP with AbstractJsonpResponseBodyAdvice
Find the our class which will support JSONP response.
JsonpAdvice.java

@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}

In the constructor, we need to call super method passing a key. This key will be used in query string of URL while requesting JSONP data. The above method facilitates REST web service to respond JSONP data and also controller method which respond using @ResponseBody and ResponseEntity.
JSONP with Spring REST
Find the bean being used in the example to generate JSON.
Book.java

public class Book {
private String bookName;
private String writer;
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
}

We are creating a web service which will respond JSONP.
BookService.java

@RestController
class BookService {
@RequestMapping(value= "/book1", produces = MediaType.APPLICATION_JSON_VALUE)
Book bookInfo1() {
Book book = new Book();
book.setBookName("Godan");
book.setWriter("Premchand");
return book;
}
}

When we access the URL http://localhost:8080/concretepage-1/book1?callback=functionCall, it will throw the response as

functionCall({"bookName":"Godan","writer":"Premchand"});

JSONP with @ResponseBody and ResponseEntity
Now find a controller in which we have created methods that will return @ResponseBody and ResponseEntity.

@Controller
class BookController {
@RequestMapping(value ="/book2", produces =MediaType.APPLICATION_JSON_VALUE )
@ResponseBody
Book bookInfo2() {
Book book = new Book();
book.setBookName("Ramcharitmanas");
book.setWriter("TulasiDas");
return book;
}
@RequestMapping(value ="/book3", produces =MediaType.APPLICATION_JSON_VALUE )
public ResponseEntity<Book> bookInfo3() {
Book book = new Book();
book.setBookName("Ramayan");
book.setWriter("Valmiki");
return ResponseEntity.accepted().body(book);
}
}

When we access the URL http://localhost:8080/concretepage-1/book2?callback=functionCall, we will get the output in browser as

functionCall({"bookName":"Ramcharitmanas","writer":"TulasiDas"});

And for the URL http://localhost:8080/concretepage-1/book3?callback=functionCall , the output will be

functionCall({"bookName":"Ramayan","writer":"Valmiki"});

JSONP Client Code
Now we will write client code which can be used in any other domain.
jsonptest.html

<html>
<head>
<script>
function functionCall(data) {
console.log(data.bookName);
console.log(data.writer);
console.log('-----------');
}
</script>
</head>
<body>
<!-- Using REST URL-->
<script src="http://localhost:8080/concretepage-1/book1?callback=functionCall" type="application/javascript"> </script> <!--Using @ResponseBody -->
<script src="http://localhost:8080/concretepage-1/book2?callback=functionCall" type="application/javascript"> </script> <!--Using ResponseEntity -->
<script src="http://localhost:8080/concretepage-1/book3?callback=functionCall" type="application/javascript"> </script>
</body>
</html>

Run this page and check the output in console.

在Firefox,chrome,opera,safari,ie9,ie8等高级浏览器直接可以用JSON对象的stringify()和parse()方法。
JSON.stringify(obj)将JSON转为字符串。JSON.parse(string)将字符串转为JSON格式;
上面的转换可以这么写:
var a={"name":"tom","sex":"男","age":"24"};
var b='{"name":"Mike","sex":"女","age":"29"}';
var aToStr=JSON.stringify(a);
var bToObj=JSON.parse(b);
alert(typeof(aToStr)); //string
alert(typeof(bToObj));//object

代码示例:

https://github.com/helloworldtang/cors-demo

参考:
http://www.cnblogs.com/softidea/p/3907113.html

http://blog.csdn.net/u010537398/article/details/52012548

https://en.wikipedia.org/wiki/JSONP

https://stackoverflow.com/questions/10323625/how-to-support-jsonp-with-spring-mvc-and-multiple-response-types

https://tonghuashuo.github.io/blog/jsonp.html

http://www.concretepage.com/spring-4/spring-4-mvc-jsonp-example-with-rest-responsebody-responseentity

http://blog.csdn.net/caiwenfeng_for_23/article/details/45300739

http://www.jianshu.com/p/983642ad125a

http://www.codesnoopy.com/2016/07/09/%E7%94%A8Spring-MVC-4%E5%AE%9E%E7%8E%B0jsonp%E8%B7%A8%E5%9F%9F%E8%B0%83%E7%94%A8/

https://my.oschina.net/u/130771/blog/52912

CORS 实战 专题的更多相关文章

  1. arcpy.mapping实战-专题图制图自动化

    arcpy.mapping实战-专题图制图自动化 by 李远祥 最初对arcpy.mapping感兴趣是因为一次大规模的专题地图调整的需要,由于某某单位利用ArcEngine编写的专题图出图系统,出现 ...

  2. Linux日志分析的实战专题

      来自 日志也是用户应该注意的地方之一.不要低估日志文件对网络安全的重要作用,因为日志文件能够详细记录系统每天发生的各种各样的事件.用户可以通过日志文件 检查错误产生的原因,或者在受到攻击和黑客入侵 ...

  3. 【朝夕Net社区技术专刊】Core3.1 WebApi集群实战专题---WebApi环境搭建运行发布部署篇

    欢迎大家阅读<朝夕Net社区技术专刊>第1期 原文是我在知乎发表的,现在在这里分享! 我们致力于.NetCore的推广和落地,为更好的帮助大家学习,方便分享干货,特创此刊!很高兴你能成为首 ...

  4. 🏆【Java技术专区】「开发实战专题」Lombok插件开发实践必知必会操作!

    前言 在目前众多编程语言中,Java 语言的表现还是抢眼,不论是企业级服务端开发,还是 Andorid 客户端开发,都是作为开发语言的首选,甚至在大数据开发领域,Java 语言也能占有一席之地,如Ha ...

  5. JEECG3.8 全套实战视频全部开放,免费下载!

    JEECG快速开发平台V3.8版本自去年10月份发布以来,下载使用数屡创新高,并受到众多开发者积极反馈.为帮助更多初学者能够快速上手,JEECG V3.8版本实战教程现已全面开放,免费下载!本教程深入 ...

  6. ASP.Net教程系列:多线程编程实战(一)

    Web开发中使用多线程可以增强用户体验,尤其是多用户.多任务.海量数据和资源紧张的情况下.所以我们的ASP.Net教程设立多线程编程实战专题.下面这些代码范例都是入门级的,希望对对大家学习ASP.Ne ...

  7. Kurento实战之一:KMS部署和体验

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  8. 148_赠送300家门店260亿销售额的零售企业Power BI实战示例数据

    焦棚子的文章目录 一背景 2022年即将到来之际,笔者准备在Power BI中做一个实战专题,作为实战专题最基础的就是demo数据,于是我们赠送大家一个300家门店,260亿+销售额,360万行+的零 ...

  9. 【资源】C++学习资料 - 逆天整理 - 精华无密版【最新】

    再失效就太无语了,链接都是多份的~~—————————————————基础——————————————C++环境搭建(全套)http://pan.baidu.com/s/1o6y0smY链接:http ...

随机推荐

  1. winform编程设定listview选中行

    在做项目中,需要用到listview显示数据.同时,项目要求,通过检索用户输入的数据,程序通过搜索,确定数据所在的行并通过程序设定为选中状态并高亮显示.同时,正常响应鼠标单击响应的效果,单击时,程序设 ...

  2. 对于CocoaPods的简单理解,实践安装使用过程和常见问题

    (本文是自己通过其他文章进行的自我编辑和简单修改,请大家凑活看看) 一.什么是CocoaPods CocoaPods是iOS项目的依赖管理工具,该项目源码在Github上管理.开发iOS项目不可避免地 ...

  3. err:安装程序试图挂载映像 1(缺少ISO 9660图像)

    一般出现此错误是因为你没有把相应的CentOS-6.4-i386-bin-DVD1.iso文件放入到你装系统所引导的盘中,造成找不到挂载映像文件. ubuntu-12.04.3-desktop-i38 ...

  4. fastDFS与java整合文件上传下载

    准备 下载fastdfs-client-java源码 源码地址 密码:s3sw 修改pom.xml 第一个plugins是必需要的,是maven用来编译的插件,第二个是maven打源码包的,可以不要. ...

  5. How--to-deploy-smart-contracts-on

    The following smart contract code is only an example and is NOT to be used in Production systems. pr ...

  6. AS3中的mouseEnabled与mouseChild

    InteractiveObject类的一个属性,InteractiveObject类是用户可以使用鼠标和键盘与之交互的所有显示对象的抽象基类.我们不能直接实例化InteractiveObject类.m ...

  7. Day9 进程理论 开启进程的两种方式 多进程实现并发套接字 join方法 Process对象的其他属性或者方法 守护进程 操作系统介绍

    操作系统简介(转自林海峰老师博客介绍) #一 操作系统的作用: 1:隐藏丑陋复杂的硬件接口,提供良好的抽象接口 2:管理.调度进程,并且将多个进程对硬件的竞争变得有序 #二 多道技术: 1.产生背景: ...

  8. 【备忘】Windows的命令行下设置网络代理

    在公司访问网络的时候,需要设置代理,设置浏览器的代理是相当的方便了.但有的时候要使用命令行,就需要自己设置了(貌似只要浏览器设置好了,可以直接使用命令行,但我们公司的借,需要有用户名和密码,如果没有使 ...

  9. Python函数化编程整理

    1.映射函数 items=[1,2,3,4,5] def inc(x): return x+1 list(map(inc,items)) [2, 3, 4, 5, 6] >>> a ...

  10. Mac下MySQL无my-default.cnf

    转自https://www.jianshu.com/p/628bcf8bb557 As of MySQL 5.7.18, my-default.ini is no longer included in ...