首页 > 其他分享 >CORS 实战 专题

CORS 实战 专题

时间:2022-12-27 21:07:28浏览次数:55  
标签:实战 专题 http jsonp JSON callback CORS JSONP data

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

CORS相关相关概念可参考

 

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.

 

CORS 实战 专题_JSON

 

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.

CORS 实战 专题_JSON_02


在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​

参考:

​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://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/​



标签:实战,专题,http,jsonp,JSON,callback,CORS,JSONP,data
From: https://blog.51cto.com/u_15147537/5973541

相关文章

  • 分布式架构中的 无状态 专题
     服务的无状态性,即:-服务端不保存任何客户端请求者信息-客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份带来的好处是什么呢?•客户端请求不依赖服务端的......
  • Ueditor 专题
    ​​https://github.com/xwjie/SpringBootUeditor​​ 提交表单提交表单设置按照部署编辑器的教程,完成编辑器加载把容器放到form表单里面,设置好要提交的路径,如下面代码中的......
  • Spring Security 实战:QQ登录实现
     准备工作1、在 ​​​QQ互联​​​ 申请成为开发者,并创建应用,得到APPID和APPKey。2、了解QQ登录时的 ​​​网站应用接入流程​​​。(必须看完看懂)为了方便各位测......
  • Vue3 企业级优雅实战 - 组件库框架 - 9 实现组件库 cli - 上
    上文搭建了组件库cli的基础架子,实现了创建组件时的用户交互,但遗留了cli/src/command/create-component.ts中的createNewComponent函数,该函数要实现的功能就是上文开......
  • Python实战—地图可视化
    今天给大家推送的是地图可视化的知识如何用Python实现地图可视化地图可视化常用于地理信息系统本节选用python中的自带库matplotlib实现地图可视化一起学习吧!   可视......
  • Kubernetes 跨集群流量调度实战
    背景Kubernetes问世于2015年,从一开始秉持着松耦合和可扩展的设计理念,也因此带来了Kubernetes生态的蓬勃发展。但这些大部分先限制在单一集群内,然后由于种种原因和目......
  • #yyds干货盘点# 名企真题专题:小东分苹果
    1.简述:描述果园里有一堆苹果,一共n头(n大于1小于8)熊来分,第一头为小东,它把苹果均分n份后,多出了一个,它扔掉了这一个,拿走了自己的一份苹果,接着第二头熊重复这一过程,即先均分n份......
  • 【Mybatis技术专题】MybatisPlus自带强大功能之多租户插件实现原理和实战分析(3)
    ​​http://mp.baomidou.com/guide/tenant.html​​前提介绍一般的程序应用当使用者访问不同,并且进入相对应的程序页面,则会把用户相关数据传输到后台这里。在传输的时候需要......
  • #yyds干货盘点# 名企真题专题:小易的升级之路
    1.简述:描述小易经常沉迷于网络游戏.有一次,他在玩一个打怪升级的游戏,他的角色的初始能力值为a.在接下来的一段时间内,他将会依次遇见n个怪物,每个怪物的防御力为b1,b2,b3.......
  • JavaWeb项目实战(3)软件快速下载
    前两篇文章里提到的所有文件均可在这里下载:​​https://www.lmonkey.com/tools/java​​......