jsonp跨域访问

在项目中遇到一个jsonp跨域的问题,于是仔细的研究了一番jsonp跨域的原理。搞明白了一些以前不是很懂的地方,比如:

1)jsonp跨域只能是get请求,而不能是post请求;

2)jsonp跨域的原理到底是什么;

3)除了jsonp跨域之外还有那些方法绕过“同源策略”,实现跨域访问;

4)jsonp和ajax,或者说jsonp和XMLHttpRequest是什么关系;

等等。

1.同源策略

说到跨域,首先要明白“同源策略”。同源是指:js脚本只能访问或者请求相同协议,相同domain(网址/ip),相同端口的页面。

我们知道,js脚本可以访问所在页面的所有元素。通过ajax技术,js也可以访问同一协议,同一个domain(ip),同一端口的服务器上的其他页面,请求到浏览器端之后,利用js就可以进行任意的访问。但是对于协议不同, 或者domain不同或者端口不同的服务器上的页面就无能为力了,完全不能进行请求。

下面在本地搭建两个tomcat,分别将端口设为8080,和8888,进行相关实验。显然他们的端口是不同的。演示如下:

http://localhost:8888/html4/ajax.html的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<! doctype html>
< html >
head >
meta charset="utf-8">
name="keywords" content="jsonp">
name="description" content="jsonp">
title >jsonp</ >
style type="text/css">
*{margin:0;padding:0;}
a{display:inline-block;margin:50px 50px;}
</ >
>
body >
a href="javascript:;" onclick="myAjax();">click me</ >
script type="text/javascript" src="js/jquery-1.11.1.min.js"></ >
type="text/javascript">
function myAjax(){
var xmlhttp;
if(window.XMLHttpRequest){
xmlhttp = new XMLHttpRequest();
}else{
xmlhttp = ActionXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
console.log(xmlhttp.responseText);
}
}
var url = "http://localhost:8080/minisns/json.jsp" + "?r=" + Math.random();
xmlhttp.open("Get",url,true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send();
}
>
>
>

这里为了结果不受其他js库的干扰,使用了原生的XMLHttpRequest来处理,结果如下:

我们看到8080端口的js的ajax请求无法访问8888端口的页面。原因是“同源策略不允许读取”。

既然普通的ajax不能访问,那么怎样才能访问呢?大家都知道,使用jsonp啊,那jsonp的原理是什么呢?他为什么能跨域呢?

2.jsonp跨域的原理

我们知道,在页面上有三种资源是可以与页面本身不同源的。它们是:js脚本,css样式文件,图片,像taobao等大型网站,很定会将这些静态资源放入cdn中,然后在页面上连接,如下所示,所以它们是可以链接访问到不同源的资源的。

1)<script type="text/javascript" src="某个cdn地址" ></script>

2)<link type="text/css" rel="stylesheet" href="某个cdn地址" />

3)<img src="某个cdn地址" alt=""/>

而jsonp就是利用了<script>标签可以链接到不同源的js脚本,来到达跨域目的。当链接的资源到达浏览器时,浏览器会根据他们的类型来采取不同的处理方式,比如,如果是css文件,则会进行对页面 repaint,如果是img 则会将图片渲染出来,如果是script 脚本,则会进行执行,比如我们在页面引入了jquery库,为什么就可以使用 $ 了呢?就是因为 jquery 库被浏览器执行之后,会给全局对象window增加一个属性: $ ,所以我们才能使用 $ 来进行各种处理。(另外为什么要一般要加css放在头部,而js脚本放在body尾部呢,就是为了减少repaint的次数,另外因为js引擎是单线程执行,如果将js脚本放在头部,那么在js引擎在执行js代码时,会造成页面暂停。)

利用 页面上 script 标签可以跨域,并且其 src 指定的js脚本到达浏览器会执行的特性,我们可以进行跨域取得数据。我们用一个例子来说明:

8888端口的html4项目中的jsonp.html页面代码如下:

26
>
>
>
type="text/javascript" src="js/jquery-1.11.1.js"></ type="text/javascript">
var url = "http://localhost:8080/html5/jsonp_data.js";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src',url);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
function callbackFun(data)
{
console.log(data.age);
console.log(data.name);
}
>
>
其访问的8080端口的html5项目中的jsonp_data.js代码如下:

1
callbackFun({ "age" :100, "name" : "yuanfang" })

将两个tomcate启动,用浏览器访问8888端口的html4项目中的jsonp.html,结果如下:

上面我们看到,我们从8888 端口的页面通过 script 标签成功 的访问到了8080 端口下的jsonp_data.js中的数据。这就是 jsonp 的基本原理,利用script标签的特性,将数据使用json格式用一个函数包裹起来,然后在进行访问的页面中定义一个相同函数名的函数,因为 script 标签src引用的js脚本到达浏览器时会执行,而我们有定义了一个同名的函数,所以json格式的数据,就做完参数传递给了我们定义的同名函数了。这样就完成了跨域数据交换。jsonp的含义是:json with padding,而在json数据外包裹它的那个函数,就是所谓的 padding 啦^--^

明白了原理之后,我们再看一个更加实用的例子:

8080端口的html5项目中定义一个servlet:

package com.tz.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;

@WebServlet("/JsonServlet")
public class JsonServlet extends HttpServlet 
{
    private static final long serialVersionUID = 4335775212856826743L;

    protected void doPost(HttpServletRequest request,HttpServletResponse response) 
            throws ServletException,IOException 
    {
        String callbackfun = request.getParameter("mycallback");
        System.out.println(callbackfun);    // callbackFun
        response.setContentType("text/json;charset=utf-8");
        
        User user = new User();
        user.setName("yuanfang");
        user.setAge(100);
        Object obj = JSON.toJSON(user);
        
        System.out.println(user);             com.tz.servlet.User@164ff87
        System.out.println(obj);             {"age":100,"name":"yuanfang"}
        callbackfun += "(" + obj + ")";    
        System.out.println(callbackfun);     callbackFun({"age":100,"name":"yuanfang"})
        
        response.getWriter().println(callbackfun);
    }

    void doGet(HttpServletRequest request,IOException 
    {
        this.doPost(request,response);
    }

}

在8888端口的html4项目中的jsonp.html来如下的跨域访问他:

40
41
42
43
44
div{width:600px;height:100px;margin:20px auto;}
div href="javascript:;">jsonp测试</ >
>
type="text/javascript">
function callbackFun(data)
{
console.log(111);
console.log(data.name);
//data.age = 10000000;
//alert(0000);
}
$(function(){
$("a").on("click",function(){
$.ajax({
type:"post",
url:"http://localhost:8080/html5/JsonServlet",
dataType:'jsonp',
jsonp:'mycallback',
jsonpCallback:'callbackFun',
success:function(data) {
console.log(2222);
console.log(data.age);
}
});
})
});
>
>
结果如下:

我们看到,我们成功的跨域取到了servlet中的数据,而且在我们指定的回调函数jsonpCallback:'callbackFun' 和 sucess 指定的回调函数中都进行了执行。而且总是callbackFun先执行,如果我们打开注释://data.age = 10000000; //alert(0000);

就会发现:在callbackFun中对 data 进行修改之后,success指定的回调函数的结果也会发生变化,而且通过alert(0000),我们确定了如果alert(000)没有执行完,success指定的函数就不会开始执行,就是说两个回调函数是先后同步执行的。

3.jsonp 跨域与 ajax

从上面的介绍和例子,我们知道了 jsonp 跨域的原理,是利用了script标签的特性来进行的,但是这和ajax有什么关系呢?显然script标签加载js脚本和ajax一点关系都没有,在没有ajax技术之前,script标签就存在了的。只不过是jquery的封装,使用了ajax来向服务器传递 jsonp 和 jsonpCallback 这两个参数而已。我们服务器端和客户端实现对参数 jsonp 和 jsonpCallback 的值,协调好,那么就没有必要使用ajax来传递着两个参数了,就像上面第二个例子那样,直接构造一个script标签就行了。不过实际上,我们还是会使用ajax的封装,因为它在调用完成之后,又将动态添加的script标签去掉了,我们看下相关的源码:

上面的代码先构造一个script标签,然后注册一个onload的回调,最后将构造好的script标签insert进去。insert完成之后,会触发onload回调,其中又将前面插入的script标签去掉了。其中的 代码 callback( 200,"success" ) 其实就是触发 ajax 的jsonp成功时的success回调函数,callback函数其实是一个 done 函数,其中包含了下面的代码:

因为传入的是 200 ,所以 isSuccess = true; 所以执行 "success"中的回调函数,response = ajaxHandleResponse(...) 就是我们处理服务器servelt返回的数据,我们可以调试:console.log(response.data.age); console.log(response.data.name); 看到结果。

3.jsonp 跨域与 get/post

我们知道 script,link,img 等等标签引入外部资源,都是 get 请求的,那么就决定了 jsonp 一定是 get 的,那么为什么我们上面的代码中使用的 post 请求也成功了呢?这是因为当我们指定dataType:'jsonp',不论你指定:type:"post" 或者type:"get",其实质上进行的都是 get 请求!!!从两个方面可以证明这一点:

1)如果我们将JsonServlet中的 doGet()方法注释掉,那么上面的跨域访问就不能进行,或者在 doPost() 和 doGet() 方法中进行调试,都可以证明这一点;

2)我们看下firebug中的“网络”选项卡:

我们看到,即使我们指定 type:"post",当dataType:"jsonp" 时,进行的也是 GET请求,而不是post请求,也就是说jsonp时,type参数始终是"get",而不论我们指定他的值是什么,jquery在里面将它设定为了get. 我们甚至可以将 type 参数注释掉,都可以跨域成功:

15
$( function (){
$( "a" ).on( "click" , (){
$.ajax({
//type:"post",
url: "http://localhost:8080/html5/JsonServlet" dataType: 'jsonp' jsonp: 'mycallback' jsonpCallback: 'callbackFun' success: (data) {
console.log(2222);
console.log(data.age);
}
});
})
});

所以jsonp跨域只能是get,jquery在封装jsonp跨域时,不论我们指定的是get还是post,他统一换成了get请求,估计这样可以减少错误吧。其对应的query源码如下所示:

Handle cache's special case and global jQuery.ajaxPrefilter( "script",function( s ) { if ( s.cache === undefined ) { s.cache = false; } if ( s.crossDomain ) { s.type = "GET"; s.global = false; } });

if( s.crossDomain){ s.type = "GET"; ...} 这里就是真相!!!!!!!!在ajax的过滤函数中,只要是跨域,jquery就将其type设置成"GET",真是那句话:在源码面前,一切了无秘密!jquery源码我自己很多地方读不懂,但是并不妨碍我们去读,去探索!

4.除了jsonp跨域方法之外的其他跨域方法

其实除了jsonp跨域之外,还有其他方法绕过同源策略,

1)因为同源策略是针对客户端的,在服务器端没有什么同源策略,是可以随便访问的,所以我们可以通过下面的方法绕过客户端的同源策略的限制:客户端先访问 同源的服务端代码,该同源的服务端代码,使用httpclient等方法,再去访问不同源的 服务端代码,然后将结果返回给客户端,这样就间接实现了跨域。相关例子,参见博文:http://www.cnblogs.com/digdeep/p/4198643.html

2)在服务端开启cors也可以支持浏览器的跨域访问。cors即:Cross-Origin Resource Sharing 跨域资源共享。jsonp和cors的区别是jsonp几乎所有浏览器都支持,但是只能是get,而cors有些老浏览器不支持,但是get/post都支持,cors的支持情况,可以参见下图(来自:http://caniuse.com/#search=cors)

cors实例:

项目html5中的Cors servlet:

class Cors long serialVersionUID = 1L; void doGet(HttpServletRequest request,HttpServletResponse response) ); response.setHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS"); response.setHeader("Access-Control-Allow-Headers","Content-Type"); response.getWriter().write("cors get"); } void doPost(HttpServletRequest request,IOException { response.setHeader("Access-Control-Allow-Origin","Content-Type"); response.getWriter().write("cors post"); } }

在html4项目中访问他:

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="keywords" content="jsonp">
    <meta name="description" content="jsonp">
    <title>cors</title>
    <style type="text/css">
        *{margin:0;padding:0;}
        div{width:600px;height:100px;margin:20px auto;}
    </style>
</head>
<body>
    <div>
        <a href="javascript:;">cors测试</a>
    </div>
    
<script type="text/javascript" src="js/jquery-1.11.1.js"></script>
<script type="text/javascript">
$(function(){
    $("a").on("click",function(){        
        $.ajax({
            type:"post",url:"http://localhost:8080/html5/cors",success:function(data) {
                console.log(data);
                alert(data);
            }
        });
    })
});    
</script>
</body>
</html>

访问结果如下:

5. 参数jsonp 和 jsonpCallback

jsonp指定使用哪个名字将回调函数传给服务端,也就是在服务端通过 request.getParameter(""); 的那个名字,而jsonpCallback就是request.getParamete("")取得的值,也就是回调函数的名称。其实这两个参数都可以不指定,只要我们是通过 success : 来指定回调函数的情况下,就可以省略这两个参数,jsnop如果不知道,默认是 "callback",jsnpCallback不指定,是jquery自动生成的一个函数名称,其对应源码如下:

12
var oldCallbacks = [],
rjsonp = /(=)\?(?=&|$)|\?\?/;
// Default jsonp settings
jQuery.ajaxSetup({
"callback" jsonpCallback: () {
callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
this [ callback ] = true ;
return callback;
}
});

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读2.4k次。最近要优化cesium里的热力图效果,浏览了网络上的各种方法,发现大多是贴在影像上的。这么做好是好,但是会被自生添加的模型或者其他数据给遮盖。其次是网上的方法大多数是截取成一个矩形。不能自定义的截取自己所需要的。经过尝试,决定修改下cesium heatmap,让他达到我们需要的要求。首先先下载 cesium heatmap包。其中我们可以看到也是通过叠加entity达到添加canvas的方法绘制到地图上。我们先把这一段代码注释} else {} };
文章浏览阅读1.2w次,点赞3次,收藏19次。在 Python中读取 json文件也可以使用 sort ()函数,在这里我介绍一个简单的示例程序: (4)如果我们想将字符串转换为列表形式,只需要添加一个变量来存储需要转换的字符串即可。在上面的代码中,我们创建了一个名为` read`的对象,然后在文件的开头使用`./`关键字来命名该对象,并在文件中定义了一个名为` json`的变量,并在其中定义了一个名为` json`的字段。比如,我们可以使用 read方法读取 json文件中的内容,然后使用 send方法将其发送到 json文件中。_python怎么读取json文件
文章浏览阅读1.4k次。首字母缩略词 API 代表应用程序编程接口,它是一种设备,例如用于使用编程代码发送和检索数据的服务器。最常见的是,该技术用于从源检索数据并将其显示给软件应用程序及其用户。当您访问网页时,API 的工作方式与浏览器相同,信息请求会发送到服务器,如何在 Windows PC 中手动创建系统还原点服务器会做出响应。唯一的区别是服务器响应的数据类型,对于 API,数据是 JSON 类型。JSON 代表 JavaScript Object Notation,它是大多数软件语言中 API 的标准数据表示法。_api是什么 python
文章浏览阅读802次,点赞10次,收藏10次。解决一个JSON反序列化问题-空字符串变为空集合_cannot coerce empty string ("") to element of `java.util.arraylist
文章浏览阅读882次。Unity Json和Xml的序列化和反序列化_unity json反序列化存储换行
文章浏览阅读796次。reader.readAsText(data.file)中data.file的数据格式为。使用FileReader对象读取文件内容,最后将文件内容进行处理使用。_a-upload 同时支持文件和文件夹
文章浏览阅读775次,点赞19次,收藏10次。fastjson是由国内的阿里推出的一种json处理器,由java语言编写,无依赖,不需要引用额外的jar包,能直接运行在jdk环境中,它的解析速度是非常之快的,目前超过了所有json库。提示:以下是引用fastjson的方法,数据未涉及到私密信息。_解析器用fastjson还是jackson
文章浏览阅读940次。【Qt之JSON文件】QJsonDocument、QJsonObject、QJsonArray等类介绍及使用_使用什么方法检查qjsondocument是否为空
文章浏览阅读957次,点赞34次,收藏22次。主要内容原生 ajax重点重点JSON熟悉章节目标掌握原生 ajax掌握jQuery ajax掌握JSON第一节 ajax1. 什么是ajaxAJAX 全称为,表示异步的Java脚本和Xml文件,是一种异步刷新技术。2. 为什么要使用ajaxServlet进行网页的变更往往是通过请求转发或者是重定向来完成,这样的操作更新的是整个网页,如果我们只需要更新网页的局部内容,就需要使用到AJAX来处理了。因为只是更新局部内容,因此,Servlet。
文章浏览阅读1.4k次,点赞45次,收藏13次。主要介绍了JsonFormat与@DateTimeFormat注解实例解析,文中通过示例代码介绍的非常详细,对大家的学习 或者工作具有一定的参考学习价值,需要的朋友可以参考下 这篇文章主要介绍了从数据库获取时间传到前端进行展示的时候,我们有时候可能无法得到一个满意的时间格式的时间日期,在数据库中显 示的是正确的时间格式,获取出来却变成了时间戳,@JsonFormat注解很好的解决了这个问题,我们通过使用 @JsonFormat可以很好的解决:后台到前台时间格式保持一致的问题,
文章浏览阅读1k次。JsonDeserialize:json反序列化注解,作用于setter()方法,将json数据反序列化为java对象。可以理解为用在处理接收的数据上。_jsondeserialize
文章浏览阅读2.7k次。labelme标注的json文件是在数据标注时产生,不能直接应用于模型训练。各大目标检测训练平台或项目框架均有自己的数据格式要求,通常为voc、coco或yolo格式。由于yolov8项目比较火热,故此本博文详细介绍将json格式标注转化为yolo格式的过程及其代码。_labelme json 转 yolo
文章浏览阅读790次,点赞26次,收藏6次。GROUP_CONCAT_UNORDERED(): 与GROUP_CONCAT类似,但不保证结果的顺序。COUNT_DISTINCT_AND_ORDERED(): 计算指定列的不同值的数量,并保持结果的顺序。COUNT_ALL_DISTINCT(): 计算指定列的所有不同值的数量(包括NULL)。AVG_RANGE(): 计算指定列的最大值和最小值之间的差异的平均值。JSON_OBJECT(): 将结果集中的行转换为JSON对象。COUNT_DISTINCT(): 计算指定列的不同值的数量。_mysql json 聚合
文章浏览阅读1.2k次。ajax同步与异步,json-serve的安装与使用,node.js的下载_json-serve 与node版本
文章浏览阅读1.7k次。`.net core`提供了Json处理模块,在命名空间`System.Text.Json`中,下面通过顶级语句,对C#的Json功能进行讲解。_c# json
文章浏览阅读2.8k次。主要介绍了python对于json文件的读写操作内容_python读取json文件
文章浏览阅读770次。然而,有时候在处理包含中文字符的Json数据时会出现乱码的情况。本文将介绍一种解决Json中文乱码问题的常见方法,并提供相应的源代码和描述。而某些情况下,中文字符可能会被错误地编码或解码,导致乱码的出现。通过适当地控制编码和解码过程,我们可以有效地处理包含中文字符的Json数据,避免乱码的发生。通过控制编码和解码过程,我们可以确保Json数据中的中文字符能够正确地传输和解析。为了解决这个问题,我们可以使用C#的System.Text.Encoding类提供的方法进行编码和解码的控制。_c# json 中文编码
文章浏览阅读997次。【代码】【工具】XML和JSON互相转换。_xml 转json
文章浏览阅读1.1k次。json path 提取数据_jsonpath数组取值
文章浏览阅读3w次,点赞35次,收藏36次。本文主要介绍了pandas read_json时ValueError: Expected object or value的解决方案,希望能对学习python的同学们有所帮助。文章目录1. 问题描述2. 解决方案_valueerror: expected object or value