今天来记录一下关于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.";

?>

跨域请求

刚才是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.")";

?>

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

来看下最终的代码执行效果。

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)";

?>

最后来查看一下跨域的效果吧。


总结

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

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

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

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


参考链接:

ajax跨域问题解决方案的更多相关文章

  1. java、ajax 跨域请求解决方案('Access-Control-Allow-Origin' header is present on the requested resource. Origin '请求源' is therefore not allowed access.)

      1.情景展示 ajax调取java服务器请求报错 报错信息如下: 'Access-Control-Allow-Origin' header is present on the requested ...

  2. 【JS】AJAX跨域-JSONP解决方案(一)

    AJAX跨域介绍 AJAX 跨域访问是用户访问A网站时所产生的对B网站的跨域访问请求均提交到A网站的指定页面 由于安全方面的原因, 客户端js使用xmlhttprequest只能用来向来源网站发送请求 ...

  3. ajax跨域请求解决方案

    大家好,今天我们学习了js的跨域请求的解决方案,由于JS中存在同源策略,当请求不同协议名,不同端口号.不同主机名下面的文件时,将会违背同源策略,无法请求成功!需要进行跨域处理! 方案一.后台PHP进行 ...

  4. Ajax跨域访问解决方案

    No 'Access-Control-Allow-Origin' header is present on the requested resource. 当使用ajax访问远程服务器时,请求失败,浏 ...

  5. No 'Access-Control-Allow-Origin' Ajax跨域访问解决方案

    No 'Access-Control-Allow-Origin' header is present on the requested resource. 当使用ajax访问远程服务器时,请求失败,浏 ...

  6. ajax跨域问题解决方案(jsonp,cors)

    跨域 跨域有三个条件,满足任何一个条件就是跨域 1:服务器端口不一致 2:协议不一致 3:域名不一致 解决方案: 1.jsonp 在远程服务器上设法动态的把数据装进js格式的文本代码段中,供客户端调用 ...

  7. ajax 跨域请求解决方案

    1.为什么出现跨域: 前端和后端同一个项目下,ajax请求的地址是localhost同一个端口是话,是不会出现跨域问题的,所以相反前端和后端分开时,ajax请求的地址或者端口不是跟后台相同时就会出现跨 ...

  8. PHP下ajax跨域的解决方案之jsonp

    首先要说明一下json和jsonp的区别? json是一种基于文本的数据交换方式,或者叫做描述数据的一种格式. var person = { "name": "test& ...

  9. AJAX 跨域 CORS 解决方案

    本篇文章由:http://xinpure.com/solutions-for-cross-domain-ajax-cors/ 两种跨域方法 在 Javascript 中跨域访问是比较常见的事情 就像现 ...

随机推荐

  1. (转)C#自制Web 服务器开发:用C#开发自己的Web服务器

    当输入:127.0.0.1:5050 GET / HTTP/1.1 Accept: text/html, application/xhtml+xml, */* Accept-Language: zh- ...

  2. [BZOJ4010]菜肴制作

    Description 知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴. ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1到N的顺序编号,预估质量最高的菜肴编号 ...

  3. 打开PS是出现“该内存不能为read”是怎么回事?

    打开PS是出现“该内存不能为read”是怎么回事? 答:内存不能为read修复工具可以有效修复计算机运行应用程序时提示:该内存不能为read要终止程序的问题,一般XP系统才会出现这个问题. 指令修复法 ...

  4. 机器学习笔记—混合高斯和 EM 算法

    本文介绍密度估计的 EM(Expectation-Maximization,期望最大). 假设有 {x(1),...,x(m)},因为是无监督学习算法,所以没有 y(i). 我们通过指定联合分布 p( ...

  5. apollo各协议支持的客户端

    apollo 源自 activemq,以快速.可靠著称,支持多协议:STOMP, AMQP, MQTT, Openwire, SSL, and WebSockets,下面就STOMP, AMQP, M ...

  6. (转)浅谈SQL Server 对于内存的管理

    简介 理解SQL Server对于内存的管理是对于SQL Server问题处理和性能调优的基本,本篇文章讲述SQL Server对于内存管理的内存原理. 二级存储(secondary storage) ...

  7. bootstrap系统学习

    1.响应式中注意的内容: 一行(row)必须在.container中. col-xs- col-sm- col-md- col-lg- 列偏移 .col-md-offset-* 列排序 .col-md ...

  8. java网络编程之图片上传

    输入输出流核心代码 所有的文件传输都是靠流,其中文件复制最具代表性.输入流和输出流,从输入流中读取数据写入到输出流中. InputStream in = 输入源; OutputStream os = ...

  9. 1-18 编译安装内核支持ntfs文件系统

    大纲: 源码编译Linux内核 使用Linux内核模块 实战:编译一个NTFS内核模块,实现Linux挂载NTFS文件系统并实现读写功能 =============================== ...

  10. C#/JAVA 程序员转GO/GOLANG程序员笔记大全(DAY 03)

    go语言当中,没有 class 的概念,那么面向对象的编程思想如何展现呢,go语言中对结构体的使用 struct. package main import "fmt" type P ...