侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

详解ajax跨域问题解决方案

前端  /  管理员 发布于 4年前   218

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

跨域

同源策略限制

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

解决方式

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

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

方式一

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内容如下:

<?phpecho "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,微信号。然后就托和自己班的女生C小兰。来帮自己去要。所以小兰就相当于一个代理。帮助小明获取原本不能直接获取的小红的联系方式。

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

直接的跨域请求

修改一下刚才的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跨域问题,就算是解决的差不多了。对我个人而言,对于这三种方式有一点点自己的看法。

  1. 服务器设置Access-Control-Allow-Origin的方式适合信用度高的小型应用或者个人应用。
  2. 代理模式则比较适合大型应用的处理。但是需要一个统一的规范,这样管理和维护起来都会比较方便。
  3. JSONP方式感觉还是比较鸡肋的(有可能是我经验还不足,没认识到这个方式的优点吧(⊙n⊙)b)。自己玩玩知道有这么个东西好了。维护起来实在是优点麻烦。

参考链接:

Ajax跨域请求: 72703.htm

服务器端跨域设置: 104442.htm

Ajax高级笔记: 116878.htm

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


  • 上一条:
    AJAX分页效果简单实现
    下一条:
    js原生map实现的方法总结
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 使用 Alpine.js 排序插件对元素进行排序(0个评论)
    • 在js中使用jszip + file-saver实现批量下载OSS文件功能示例(0个评论)
    • 在vue中实现父页面按钮显示子组件中的el-dialog效果(0个评论)
    • 使用mock-server实现模拟接口对接流程步骤(0个评论)
    • vue项目打包程序实现把项目打包成一个exe可执行程序(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-10
    • 2017-11
    • 2018-03
    • 2018-04
    • 2018-05
    • 2018-06
    • 2018-09
    • 2018-11
    • 2018-12
    • 2019-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2021-04
    • 2021-05
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客