Web前端之路让前端飞前端开发那些事

Ajax 跨域解决方案

2017-11-17  本文已影响186人  卓三阳

在解决Ajax跨域问题之前,我们先一起来看看什么是浏览器的同源策略

一.浏览器同源政策

协议相同
域名相同
端口相同

举例来说,http://www.guangzhou.com/p/page.html这个网址,协议是http://,域名是 www.guangzhou.com,端口是80(默认端口可以省略)。它的同源情况如下。

http://www.guangzhou.com/dir/other.html: 同源
http://guangzhou.com/dir/other.html: 不同源(域名不同)
http://v2.www.guangzhou.com/dir/other.html:不同源(域名不同)
http://www.guangzhou.com:81/dir/other.html:不同源(端口不同)

(1) Cookie、LocalStorage 和 IndexDB 无法读取。
(2) DOM 无法获得。
(3) AJAX 请求不能发送。

虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。

在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但img、iframe、script等标签是个例外,这些标签可以通过src属性请求到其他服务器上的数据。

我们下面只专注于ajax跨域请求!!!


二.如何解决ajax跨域

一般ajax跨域就是通过代理,JSONP或者CORS三种方式解决(注意,现在JSONP很少用了,所以了解下即可)

(1)JSONP方式解决跨域问题

jsonp解决跨域问题是一个比较古老的方案(实际中不推荐使用),这里做简单介绍(实际项目中如果要使用JSONP,一般会使用JQ等对JSONP进行了封装的类库来进行ajax请求)
实现原理
JSONP之所以能够用来解决跨域方案,主要是因为 <script> 脚本拥有跨域能力,而JSONP正是利用这一点来实现。具体原理如图

jsonp-theory.png

实现流程
客户端网页网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制
当我们通过JSONP模式请求跨域资源时,服务器返回给客户端一段javascript代码,这段javascript代码自动调用客户端回调函数。
客户端

jsonp.png

服务器端(PHP)

<?php
$staff = array
    (
        array("name" => "洪七", "number" => "101", "sex" => "男", "job" => "总经理"),
        array("name" => "郭靖", "number" => "102", "sex" => "男", "job" => "开发工程师"),
        array("name" => "黄蓉", "number" => "103", "sex" => "女", "job" => "产品经理")
    );

$jsonp = $_GET["callback"];
    //检查是否有员工编号的参数
if (!isset($_GET["number"]) || empty($_GET["number"])) {
        echo $jsonp . '({"success":false,"msg":"参数错误"})';
        return;
    }

//获取number参数
$number = $_GET["number"];
$result = $jsonp . '({"success":false,"msg":"没有找到员工。"})';
    
//遍历$staff多维数组,查找key值为number的员工是否存在,如果存在,则修改返回结果
foreach ($staff as $value) {
    if ($value["number"] == $number) {
        $result = $jsonp . '({"success":true,"msg":"找到员工:员工编号:' . $value["number"] .
                            ',员工姓名:' . $value["name"] . 
                            ',员工性别:' . $value["sex"] . 
                            ',员工职位:' . $value["job"] . '"})';
        break;
        }
    }
 echo $result;

由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。
注意,一般的JSONP接口和普通接口返回数据是有区别的,所以接口如果要做JSONO兼容,需要进行判断是否有对应callback关键字参数,如果有则是JSONP请求,返回JSONP数据,否则返回普通数据

附(JQ对JSONP进行了封装的类库来进行ajax请求):


Jq-Jsonp.png

使用注意
基于JSONP的实现原理,所以JSONP只能是“GET”请求,不能进行较为复杂的POST和其它请求,所以遇到那种情况,就得参考下面的CORS解决跨域了(所以如今它也基本被淘汰了)

(2)CORS解决跨域问题

CORS请求原理
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

基本上目前所有的浏览器都实现了CORS标准(IE10以下不支持),其实目前几乎所有的浏览器ajax请求都是基于CORS机制的,只不过可能平时前端开发人员并不关心而已(所以说其实现在CORS解决方案主要是考虑后台该如何实现的问题)。

cors-theory.png

如何判断是否是简单请求?

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。只要同时满足以下两大条件,就属于简单请求。

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type(只限于三个值application/x-www-form-urlencoded、 multipart/form-data、text/plain)

凡是不同时满足上面两个条件,就属于非简单请求。

实现——PHP后台配置
PHP后台的配置几乎是所有后台中最为简单的,遵循如下步骤即可:
第一步:配置Php 后台允许跨域

<?php
header('Access-Control-Allow-Origin:*');  //支持全域名访问,不安全,部署后需要固定限制为客户端网址
header('Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE'); //支持的http 动作
header('Access-Control-Allow-Headers:x-requested-with,content-type');  //响应头 请按照自己需求添加。

实现——Node.js后台配置(express框架)

app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By", ' 3.2.1')
        //这段仅仅为了方便返回json而已
    res.header("Content-Type", "application/json;charset=utf-8");
    if(req.method == 'OPTIONS') {
        //让options请求快速返回
        res.sendStatus(200); 
    } else { 
        next(); 
    }
});
(3)代理请求方式解决跨域问题

这种方式是通过后台(ASP、PHP、JAVA、ASP.NET)获取其他域名下的内容,然后再把获得内容返回到前端,这样因为在同一个域名下,所以就不会出现跨域的问题。

实现过程
比如在广州的WEB服务器的后台(www.guangzhou.com/proxy-shanghaiservice.php)来调用上海(www.shanghai.com/service.php)的服务,然后把响应的结果返回前端,这样前端调用广州同域名的服务就和调用上海的服务效果相同了。


参考资料:
浏览器同源政策及其规避方法

上一篇 下一篇

猜你喜欢

热点阅读