跨域&JSONP

1. 同源&跨域

1.1 什么是同源?什么是跨域?

  • 同源:协议头、域名、端口完全一致就叫同源

  • 跨域:协议头、域名、端口有一个不一样就叫跨域

  • 判断是否同源,如:http://www.example.com/detail.html 与以下地址对比

对比地址 是否同源 原因
http://api.example.com/detail.html 不同源 域名不同
https://www.example.com/detail.html 不同源 协议不同
http://www.example.com:8080/detail.html 不同源 端口不同
http://api.example.com:8080/detail.html 不同源 域名、端口不同
https://api.example.com/detail.html 不同源 协议、域名不同
https://www.example.com:8080/detail.html 不同源 端口、协议不同
http://www.example.com/other.html 同源 只是目录不同

 

 

 

 

 

 

 

 

 

1.2 跨域的影响

  • 注:根据同源和跨域的意思,跨域就是指不同源
  • 如果要在 localhost/test.html 这个网站*问 http://api.douban.com/v2/movie/top250 这个接口,那么浏览器会出现以下错误:

跨域&JSONP

  • 原因:浏览器本身从安全角度考虑,不支持跨域访问

2. JSONP

2.1 如何突破跨域请求限制

2.1.1 使用img标签(拿不到响应体)

我们知道:img 标签,有个 href 属性,这个属性其实也是向服务器发送请求,我们看看这种方式是否可以跨越访问,代码如下:

<img href="http://api.douban.com/v2/movie/top250" alt="">

此时可以看到能正确收到响应,但拿不到响应体(返回的JSON数据),如图:

跨域&JSONP

2.1.2 使用link标签(拿不到响应体)

link 标签,有个 src 属性,我们以前是用来外联css样式,其实它也是相当于发送请求

 <link rel="stylesheet" src="http://api.douban.com/v2/movie/top250"

 看浏览器响应报文,可以发现link标签也可以跨域请求,但是一样,没办法拿到响应体

2.1.3 使用script标签(曲线拿到响应体)

代码如下:

1 <script>
2     function test(json) {
3         console.log('我被调用了');
4         console.log(json);
5     }
6 </script>
7 <script src="http://api.douban.com/v2/movie/top250?callback=test"></script>

结果如下图

跨域&JSONP

 说明:我们可以看到,我们预先定义了一个函数叫test,再然后在src里加了一个参数callback=test,可以发现,当请求完成,会自动调用test这个函数,并且把响应体(JSON数据)当做参数传递过来

2.2 使用script标签拿到JSON数据

2.2.1 script导入外部文件作用

  • 使用script导入外部文件时,默认会把文件内容当JS代码执行,例:

  • 新建 1.txt 文件,文件内容如下:

alert('你好');
  • 新建 index.html 文件,核心代码如下:
<script src="1.txt"></script>

结果:

跨域&JSONP

结论:script导入文件会默认把文件内容当JS代码执行,跟文件格式无关

2.2.2 script标签曲线拿到响应体原理

  • 新建 index.html 页面,代码如下
1 <script>
2     //声明一个success函数
3     function success(obj){
4         console.log(obj);
5     }
6 </script>
7 <!-- script导入文件 -->
8 <script src="http://www.demo.com/top250.php"></script>
  • 新建 top250.php 页面,代码如下
echo "success('hello')";

结果

跨域&JSONP

说明:请求的top250文件里,服务器最终返回的是 success('hello'); 这个当JS代码执行时,是调用函数并传递参数hello的语法,因此可以看到success函数被调用,并且打印了参数hello

图解:

跨域&JSONP

★★ 思考:如果在上例中,把success函数的参数换成json对象,会怎样?

  • 修改 top250.php 代码,如下:
1     $data = array('name' => 'jack','age'=>16,'gender'=>'true' );
2 
3     $json = json_encode($data);
4 
5     echo "success({$json})"; 
  • 再次访问网页可以发现,打印的参数就变成了json对象

跨域&JSONP

结论:script标签拿到JSON数据的原理就是在服务器端写调用浏览器端函数的方法 ,然后把JSON数据当参数传递过去

★★ 思考:现在服务端代码这么写,如果要解决跨域问题,是不是每个浏览器端都要准备一个名叫success的函数?能让浏览器端自己指定吗?怎么指定?

  • 请求数据时加参数,修改 index.html
1 <script>
2     //声明函数
3     function myFunc(obj){
4         console.log(obj);
5     }
6 </script>
7 
8 <!-- 请求数据,并加入callback参数,值为myFunc -->
9 <script src="http://www.demo.com/top250.php?callback=myFunc"></script>
  • 此时,除了请求 top250.php 外,还传递了个 callback 的参数过去,修改 top250.php,拿到这个参数
 1 <?php
 2 
 3     //拿到传递过来的参数,即函数名
 4     $funcName = $_GET['callback'];
 5 
 6     //准备数据
 7     $data = array('name' => 'jack','age'=>16,'gender'=>true );
 8 
 9     //转化为JSON
10     $json = json_encode($data);
11 
12     //传过来的是什么函数名,就调用什么函数
13     echo "$funcName($json)"; 
14 ?>

结果:此时就完成了指定函数名让服务器调用传参的效果了。

这就是之前跨域拿数据的原理,即服务器端调用浏览器端的函数,把参数传递过来即可

2.3 JSONP概念总结

  • JSONP:

    • JSON with Padding 是一种借助于script标签发送跨域请求的技巧方案。

  • 它不是一套新技术,只是聪明的程序员想出的一套方案

  • 能不能用这套方案,要看服务器端代码怎么写,服务器端如果写了调用函数的代码,那么就能支持JSONP方案

  • 服务器的接口会有接口文档进行说明是否支持JSONP

  • 建议:不要随意使用别人的接口,特别是别人写的支持JSONP的接口

    例:https://developers.douban.com/wiki/?title=api_v2

3. CORS

  • Cross Origin Resource Share,跨域资源共享

  • 直接从服务器端解决ajax无法跨域访问的问题(可以不要用jsonp了)

// 允许远端访问
header('Access-Control-Allow-Origin: *');

这种方案无需客户端作出任何变化(客户端不用改代码),只是在服务端添加一个 Access-Control-Allow-Origin 的响应头,表示这个资源是否允许指定域请求(*代表任何网站都可以访问)。