ajax跨域问题解决方案

原创 2017年06月20日 17:05:15

今天来记录一下关于ajax跨域的一些问题。以备不时之需。

跨域

同源策略限制

同源策略阻止从一个域上加载的脚本获取或操作另一个域上的文档属性。也就是说,受到请求的 URL 的域必须与当前 Web 页面的域相同。这意味着浏览器隔离来自不同源的内容,以防止它们之间的操作。

解决方式

通常来说,比较通用的有如下两种方式,一种是从服务器端下手,另一种则是从客户端的角度出发。二者各有利弊,具体要使用哪种方式还需要具体的分析。

  • 服务器设置响应头
  • 服务器代理
  • 客户端采用脚本回调机制。

方式一

Access-Control-Allow-Origin 关键字只有在服务器端进行设置才
会生效。也就是说即使再客户端使用

xmlhttprequest.setHeaderREquest('xx','xx');

也不会有什么效果。

正常ajax请求

下面来模拟一下ajax非跨域请求的案例实现。

test1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax 测试</title>
</head>
<body>

<input type="button" value="Test" onclick="crossDomainRequest()">
<div id="content"></div>
<script>
    var xhr = new XMLHttpRequest();
    var url = 'http://localhost/learn/ajax/test1.php';

    function crossDomainRequest() {
        document.getElementById('content').innerHTML = "<font color='red'>loading...</font>";
        // 延迟执行
        setTimeout(function () {
            if (xhr) {
                xhr.open('GEt', url, true);
                xhr.onreadystatechange = handle_response;
                xhr.send(null);
            } else {
                document.getElementById('content').innerText = "不能创建XMLHttpRequest对象";
            }
        }, 3000);
    }

    function handle_response() {
        var container = document.getElementById('content');
        if (xhr.readyState == 4) {
            if (xhr.status == 200 || xhr.status == 304) {
                container.innerHTML = xhr.responseText;
            } else {
                container.innerText = '不能跨域请求';
            }
        }
    }
</script>

</body>
</html>

同级目录下的test1.php内容如下:

<?php

echo "It Works.";

?>

正常ajax请求

跨域请求

刚才是HTML文件和PHP文件都在Apache的容器下,所以没有出现跨域的情形,现在把HTML文件放到桌面上,这样再次请求PHP数据的话,就营造了这样一个“跨域请求”了。

注意看浏览器的地址栏信息
再次进行访问,发现会出现下面的错误信息。
跨域出现了问题

针对这种情况,比较常见的一个操作就是设置Access-Control-Allow-Origin

格式: Access-Control-Allow-Origin: domain.com/xx/yy.*

如果知道客户端的域名或者请求的固定路径,则最好是不使用通配符的方式,来进一步保证安全性。如果不确定,那就是用*通配符好了。

后端开发语言为PHP的时候可以再文件开始处这么设置:

header("Access-Control-Allow-Origin: *");

如果是ASPX页面的话,要这么设置(Java与之类似):

Response.AddHeader("Access-Control-Allow-Origin", "*");

这时,再次来访问一下刚才的路径。
服务器端跨域设置

服务器代理模式

这种方式应该算是比较常用的,而且被广泛采纳的一个方式了。说代理有点太过于书面化了,其实就是传话儿的。来举个小例子:

小明喜欢三班一个叫小红的女孩儿,但是不好意思去要人家的QQ,微信号。然后就托和自己班的女生–小兰。来帮自己去要。所以小兰就相当于一个代理。帮助小明获取原本不能直接获取的小红的联系方式。

下面来举个例子说明这个问题。

直接的跨域请求

修改一下刚才的URL即可,让ajax直接去请求其他网站的数据。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax 测试</title>
</head>
<body>

<input type="button" value="Test" onclick="crossDomainRequest()">
<div id="content"></div>
<script>
    var xhr = new XMLHttpRequest();
//    var url = 'http://localhost/learn/ajax/test1.php';
     var url = 'http://api.qingyunke.com/api.php?key=free&appid=0&msg=%E5%93%92%E5%93%92';
    function crossDomainRequest() {
        document.getElementById('content').innerHTML = "<font color='red'>loading...</font>";
        // 延迟执行
        setTimeout(function () {
            if (xhr) {
                xhr.open('GEt', url, true);
                xhr.onreadystatechange = handle_response;
                xhr.send(null);
            } else {
                document.getElementById('content').innerText = "不能创建XMLHttpRequest对象";
            }
        }, 3000);
    }

    function handle_response() {
        var container = document.getElementById('content');
        if (xhr.readyState == 4) {
            if (xhr.status == 200 || xhr.status == 304) {
                container.innerHTML = xhr.responseText;
            } else {
                container.innerText = '不能跨域请求';
            }
        }
    }
</script>

</body>
</html>

结果如下:
代理模式下直接跨域会失败

启用代理模式

刚才的HTML页面,咱们还是用自己的接口:

url = 'http://localhost/learn/ajax/test1.php';

具体如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax 测试</title>
</head>
<body>

<input type="button" value="Test" onclick="crossDomainRequest()">
<div id="content"></div>
<script>
    var xhr = new XMLHttpRequest();
    var url = 'http://localhost/learn/ajax/test1.php';
//     var url = 'http://api.qingyunke.com/api.php?key=free&appid=0&msg=%E5%93%92%E5%93%92';
    function crossDomainRequest() {
        document.getElementById('content').innerHTML = "<font color='red'>loading...</font>";
        // 延迟执行
        setTimeout(function () {
            if (xhr) {
                xhr.open('GEt', url, true);
                xhr.onreadystatechange = handle_response;
                xhr.send(null);
            } else {
                document.getElementById('content').innerText = "不能创建XMLHttpRequest对象";
            }
        }, 3000);
    }

    function handle_response() {
        var container = document.getElementById('content');
        if (xhr.readyState == 4) {
            if (xhr.status == 200 || xhr.status == 304) {
                container.innerHTML = xhr.responseText;
            } else {
                container.innerText = '不能跨域请求';
            }
        }
    }
</script>

</body>
</html>

然后对应的test1.php应该帮助我们实现数据请求这个过程,把“小红的联系方式”要到手,并返回给“小明”

<?php

$url = 'http://api.qingyunke.com/api.php?key=free&appid=0&msg=hello%20world.';
$result = file_get_contents($url);
echo $result;

?>

下面看下代码执行的结果。
代理模式下的跨域实现


jsonp方式

JSONP(JSON with Padding) 灵感其实源于在HTML页面中script标签内容的加载,对于script的src属性对应的内容,浏览器总是会对其进行加载。于是:

克服该限制更理想方法是在 Web 页面中插入动态脚本元素,该页面源指向其他域中的服务 URL 并且在自身脚本中获取数据。脚本加载时它开始执行。该方法是可行的,因为同源策略不阻止动态脚本插入,并且将脚本看作是从提供 Web 页面的域上加载的。但如果该脚本尝试从另一个域上加载文档,就不会成功。

实现的思路就是:
在服务器端组装出客户端预置好的json数据,通过回调的方式传回给客户端。

原生实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax 测试</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.8.0.js" type="text/javascript"></script>
</head>
<body>
<input type="text" name="talk" id="talk">
<input type="button" value="Test" id="btn">
<div id="content"></div>
<script type="text/javascript">

function jsonpcallback(result) {
    for(var i in result) {
        alert(i+":"+result[i]);
    }
 }
 var JSONP = document.createElement("script");
 JSONP.type='text/javascript';
 JSONP.src='http://localhost/learn/ajax/test1.php?callback=jsonpcallback';
 document.getElementsByTagName('head')[0].appendChild(JSONP);


</script>

</body>
</html>

服务器端test1.php内容如下:

<?php

$arr = [1,2,3,4,5,6];
$result = json_encode($arr);
echo "jsonpcallback(".$result.")";

?>

需要注意的是最后组装的返回值内容。

来看下最终的代码执行效果。
JSONP原生跨域实现

JQuery方式实现

采用原生的JavaScript需要处理的事情还是蛮多的,下面为了简化操作,决定采用JQuery来代替一下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax 测试</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.8.0.js" type="text/javascript"></script>
</head>
<body>
<input type="text" name="talk" id="talk">
<input type="button" value="Test" id="btn">
<div id="content"></div>

<script type="text/javascript">

    function later_action(msg) {
        var element = $("<div><font color='green'>"+msg+"</font><br /></div>");
        $("#content").append(element);
    }

    $("#btn").click(function(){
        // alert($("#talk").val());
        $.ajax({
        url: 'http://localhost/learn/ajax/test1.php',
        method: 'post',
        dataType: 'jsonp',
        data: {"talk": $("#talk").val()},
        jsonp: 'callback',
        success: function(callback){
            console.log(callback.content);
            later_action(callback.content);
        },
        error: function(err){
            console.log(JSON.stringify(err));

        },
    });
    });
</script>

</body>
</html>

相应的,test1.php为了配合客户端聊天的需求,也稍微做了点改变。

<?php
$requestparam = isset($_GET['callback'])?$_GET['callback']:'callback';

// 青云志聊天机器人接口: http://api.qingyunke.com/api.php?key=free&appid=0&msg=hello
// 接收来自客户端的请求内容
$talk = $_REQUEST['talk'];
$result = file_get_contents("http://api.qingyunke.com/api.php?key=free&appid=0&msg=$talk");

// 拼接一些字符串
echo $requestparam . "($result)";

?>

最后来查看一下跨域的效果吧。
JSONP 跨域实现聊天应用


总结

至此,关于简单的ajax跨域问题,就算是解决的差不多了。对我个人而言,对于这三种方式有一点点自己的看法。

  • 服务器设置Access-Control-Allow-Origin的方式适合信用度高的小型应用或者个人应用。

  • 代理模式则比较适合大型应用的处理。但是需要一个统一的规范,这样管理和维护起来都会比较方便。

  • JSONP方式感觉还是比较鸡肋的(有可能是我经验还不足,没认识到这个方式的优点吧(⊙﹏⊙)b)。自己玩玩知道有这么个东西好了。维护起来实在是优点麻烦。


参考链接:

版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。转载时请标注http://blog.csdn.net/marksinoberg. 举报

相关文章推荐

编程路上,对于迷失者的一些小小建议

前几天,在半梦半醒中写了一篇《编程路上,送给处于迷茫中的你和自己》,没想到还挺受欢迎,同时收到了一些朋友的留言和感谢,意外之余也挺开心。大多人都会经历的迷茫其实这也都难免的,现在计算机技术更新那么快,...

git正确的删除远程仓库的文件并用.gitignore忽略提交此文件

git删除远程仓库的文件

精选:深入理解 Docker 内部原理及网络配置

网络绝对是任何系统的核心,对于容器而言也是如此。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker的网络一直以来都比较薄弱,所以我们有必要深入了解Docker的网络知识,以满足更高的网络需求。

Apache 使用ssl模块配置HTTPS

Web服务器在默认情况下使用HTTP,这是一个纯文本的协议。正如其名称所暗示的,纯文本协议不会对传输中的数据进行任何形式的加密。而基于HTTP的Web服务器是非常容易配置,它在安全方面有重大缺陷。任何...

MySQL日志文件之错误日志和慢查询日志详解

实验环境:MySQL Community Server (GPL) 5.7.17、Ubuntu 16.04一、MySQL日志文件分类官方文档:https://dev.mysql.com/doc/ref...

XAMPP各个版本配置

XAMPP这集成环境也很不错 如果你的PHP代码有加密部分,而Zend Guard 5.2加密的代码不能用5.3解密, 只有下载最后一个PHP5.2版本的xampp 1.7.1,并改动...

PHPWAMP自启异常,服务器重启后Apache等服务不会自动重启的原因分析

PHPWAMP是我开发的一款集成了VC的纯绿色集成环境,支持自定义设置php版本、多版本同时运行、强制修改任何环境数据库密码、一键去除域名非80端口、强制卸载任意环境、强制解除环境阻碍、自动定期任务系...
  • Lccee
  • Lccee
  • 2017-02-16 11:28
  • 19612

xamarin android异步更新UI线程

UI线程简单了解 一些从事web开发的同学,可能对UI线程没有这个概念,没办法,毕竟“UI线程”这个概念只存在一些客户端(window客户端软件、app等)。其实android在子线程中更新UI线程,...

程序员的八重境界

看到一篇有趣的文章The Eight Levels of Programmers。以前似乎看过不少这种程序员的多少个级别、境界,但这篇语言很风趣,而且分类比较细化,让人觉得挺合情合理、无法反驳的。绝大...
  • dc_726
  • dc_726
  • 2017-08-31 04:58
  • 21940

链路层的双链路--大型服务器的优化体系

链路层的双链路--大型服务器的优化体系 一直在潜水,不过朋友可在相关QQ群找我,哈也可以加我工作QQ; 之前有朋友问过,链路层的东西,其实很抽象,刚好看到相关的内容,在此分享一...

[Phonegap+Sencha Touch] 移动开发67 安卓实现 新版提示更新并下载安装(自动更新插件)

这是一个可以自动更新phonegap android app 的plugin,能显示下载进度条,下载完自动安装 插件地址: https://github.com/lovelyelfpop/andro...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)