美文网首页
Ajax的跨域问题

Ajax的跨域问题

作者: whisper330 | 来源:发表于2019-12-26 16:52 被阅读0次

什么是跨域及来源

跨域问题来源于浏览器的同源策略,JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。

什么是同源策略

同源策略是浏览器为安全性考虑实施的非常重要的安全策略。只有协议、端口、和域名相同,则允许相互访问。

下面几种情况列举了不同情况下的地址,以及能否成功访问。
  • Ajax跨域代码示例
    在这个代码中,我们创建了一个oBtn按钮对象,当点击它的时候会创建名为xhr的XMLHttpRequest对象,想让它获取api.binstd.com上的天气数据。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax</title>
</head>
<body>
    <div id="mydiv">
        <button id="btn">点击</button>
    </div>
</body>
<script type="text/javascript">
    window.onload = function() {
      var oBtn = document.getElementById('btn');
      oBtn.onclick = function() {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4 && xhr.status == 200) {
                    alert( xhr.responseText );
            }
        };
        xhr.open('get', 'https://api.binstd.com/weather2/query?appkey=0baae6a71fc7209b&city=安顺&date=2018-01-01', true);
        xhr.send(); 
    };
点击按钮发现浏览器提示如下,也就是说,我们的访问被拒绝了。这个就是实际的跨域问题。

如何解决跨域问题

1.通过JSONP解决

JSONP 是 JSON with padding(填充式 JSON 或参数式 JSON)的简写。

本质上是利用HTML元素的src属性都可以跨域的思路来解决的。

imgscriptiframe等标记的src属性的值都可以赋成其它域名的合法地址。我们回想一下,在添加img标签的时候,里面有src属性,属性的链接可以是任何图片的有效链接,这时候是没有跨域的问题的。

下图描述了用JSONP解决跨域问题的基本原理。


该过程涉及到客户端服务器端两部分:
  • 在客户端,我们在src里面写上请求地址以及回调函数func(),并把回调函数放在全局,然后向服务器发送请求
  • 服务器在收到我们的请求以后,先准备我们请求的数据,然后返回函数执行字符串'func('+JSON.stringify(data)+')'
  • 客户端执行这条语句(回调函数)。

现在我们用JSONP方案来改进Ajax跨域代码示例,代码如下。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>JSONP实现跨域2</title>
  </head>
  <body>
    <div id="mydiv">
        <button id="btn">点击</button>
    </div>
  </body>
  <script type="text/javascript">
    function handleResponse(response){
            console.log(response);
    }
  </script>
  <script type="text/javascript">
    window.onload = function() {
      var oBtn = document.getElementById('btn');
      oBtn.onclick = function() {     
        var script = document.createElement("script");
        script.src = "https://api.binstd.com/weather2/query?appkey=0baae6a71fc7209b&city=安顺&date=2018-01-01&callback=handleResponse";
        //在body的第一个孩子之前,插入script节点
        document.body.insertBefore(script, document.body.firstChild);   
    };
  };
  </script>
</html>

代码是在oBtn点击事件里面添加了一个script标签,它的src属性里面包含了请求地址和回调函数。
点击按钮,请求数据成功,控制台打印如下。


JSONP之需要注意的几点:
1、jsonp没有使用XMLHttpRequest对象。
2、jsonp只支持Get方式

2.通过CORS标准解决

细心的你可能会发现,示例代码的错误里面有这句话... origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header ...,意思是原要求被CORS政策限制了。

什么是CORS

CORS:全称"跨域资源共享"(Cross-origin resource sharing)。

CORS需要浏览器和服务器同时支持,才可以实现跨域请求,目前几乎所有浏览器都支持CORS,IE则不能低于IE10。CORS的整个过程都由浏览器自动完成,前端无需做任何设置,跟平时发送http请求并无差异。

所以,实现CORS的关键在于服务器,只要服务器实现CORS接口,就可以实现跨域通信。

CORS请求类型

CORS请求类型分为简单请求和非简单请求,后者需要预检请求。

  • 简单请求
    符合以下条件的,为简单请求
使用下列方法之一:
* GET
* HEAD
* POST
Content-Type的值仅限于下列三者之一:
* text/plain
* multipart/form-data
* application/x-www-form-urlencoded

简单请求的流程如下:


客户端:在客户端的header里面添加Origin信息,将域名写在里面,然后正常的发请求到服务器。
服务端:服务端根据Access-Control-Allow-Origin属性来判别这个请求源是否可以请求成功。
Access-Control-Allow-Origin:* 标识任何外域
Access-Control-Allow-Origin: 允许访问源(多个源用,分隔)

如果客户端的origin在上述允许源的范围内,则可以正确返回数据。这里其实相当于服务器给特定的源开一个“会员”通道,只有满足条件的才能进。

  • 非简单请求
    非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)
下图描述了该类型请求的流程:

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

举例来讲,现在发一个put请求,并添加header信息:

var url = 'https://api.binstd.com/weather2';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

浏览器发现这是一个非简单请求以后,就会自动发出“预检请求”,请求的一部分如下:

OPTIONS /cors HTTP/1.1
Origin: 原域名
Access-Control-Request-Method: PUT //请求方式
Access-Control-Request-Headers: X-Custom-Header //请求头

服务器在收到上述的预检请求以后,检查上述字段是否在“会员通道”中,如果在的话,会确认该跨源请求,并作出回应。

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: 允许的域名 //可以为特定的域名,也可以用*代表允许任何跨源请求
Access-Control-Allow-Methods: GET, POST, PUT //允许的方式
Access-Control-Allow-Headers: X-Custom-Header //允许的头信息
Content-Type: text/html; charset=utf-8
...

一旦通过了预检请求以后,后面的就和简单请求一样了。

3.使用代理服务器

有时候处于安全考虑或者因为在实际开发中多个环境,在部分环境下,我们需要前端自己解决跨域问题。这个时候我们会采用一种名为代理服务器方法。

下图为该方法的原理:



该方法涉及到客户端,代理服务器和服务器三个部分:

  • 客户端:发送请求到代理服务器上面
  • 代理服务器:代理服务器设置CORS(代理服务器可以自己设置),并将浏览器的请求转发到服务器
  • 服务器服务器和代理服务器之间是没有同源的策略的,所以服务器可以处理代理服务器的转发请求

代理服务器不是请求的生产者,只是请求的搬运工。

现在我们用代理服务器方案来进行代码示例:
需求:用代理服务器解决ajax请求天气数据的跨域问题。
TIPS:数据接口API可以上聚合数据或者是进制数据,里面有免费数据API。

这个方法中需要改变的是客户端和代理服务器的设置:

  • 客户端:正常发送ajax请求,将请求url写成代理服务器的地址
ajax({
   type: 'get',
   url: 'http://localhost:7777', //代理服务器的域名
   data: {
        city: city,
        key: ************
   },
   success: resultSuccess,
   error: function (error) {
      console.log('error', error);
    }
});

function ajax (options) {
    options = options || {};   
    options.data = options.data || {};   
    var json = json(options);   
    // ajax请求   
    function json(options) {   
      // 请求方式,默认是GET
      options.type = (options.type || 'GET').toUpperCase();
      var xhr = null;    
      // 实例化XMLHttpRequest对象   
      if(window.XMLHttpRequest) {   
        xhr = new XMLHttpRequest();   
      } else {   
        // IE6及其以下版本   
        xhr = new ActiveXObjcet('Microsoft.XMLHTTP');
      };
      // 监听事件
      xhr.onreadystatechange = function() {
        if(xhr.readyState == 4) {
          var status = xhr.status;
          if(status >= 200 && status < 300) {
            options.success(JSON.parse(xhr.responseText));
          } else {
            //options.error && options.error(status);
            options.error(status);
          }
        }
      };
      // 连接和传输数据
      if(options.type == 'GET') {
        xhr.open(options.type, options.url + '/?city='+ options.data.city + "&key=" + options.data.key, true);
        xhr.send(null);
      } else {
        xhr.open(options.type, options.url, true);
        //设置提交时的内容类型
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
        xhr.send(options.data);
      }
    }
  }   
  • 代理服务器:设置CORS标准,允许客户端访问,并将请求转发到真正的请求地址上。
//设置CORS标准的两种方式
response.writeHead(200,{"Access-Control-Allow-Origin":"http://127.0.0.1:5500/weather/index.html",
                         "Content-Type":"text/plain;charset=utf-8"});
response.setHeader("Access-Control-Allow-Origin","http://127.0.0.1:5500");
//转发请求到聚合数据API上
http.get('http://apis.juhe.cn/simpleWeather/query?' + parsedUrl.query, res => {
      var body = '';

      res.on('data', data => {
        body += data;
});

请求成功啦,header信息如下:


代理服务器之需要注意的几点
1、这里的CORS标准就是“会员通道”的通行证,我们还可以设置其他条件(详情见第二小节)。
2、当使用 response.setHeader() 设置响应头时,它们将与传给 response.writeHead() 的任何响应头合并,其中 response.writeHead() 的响应头优先。详情请移步链接[9][10]

参考链接:

[1]轻松搞定JSONP跨域请求
[2]珠峰培训官方
[3]原生JS的面试题:jsonp的实现原理
[4]HTTP访问控制(CORS)
[5]cors实现请求跨域
[6]跨域资源共享 CORS 详解
[7]跨域 CORS
[8]前端项目中nginx 本地反向代理配置
[9]response.setHeader(name, value)
[10]response.writeHead(statusCode[, statusMessage][, headers])

相关文章

  • 解决ajax跨域问题

    Jsonp解决ajax跨域问题 CORS解决ajax跨域问题

  • 前端跨域

    什么是ajax跨域 ajax跨域的原理 ajax出现请求跨域错误问题,主要原因就是因为浏览器的“同源策略”,可以参...

  • Http浅析【2】——ajax跨域问题

    视频参考:ajax跨域完全讲解 本文精华版:【综合】ajax跨域问题 什么是跨域问题 简单来讲,当前台调用后台,如...

  • ajax跨域请求

    ajax跨域请求(jsonp) 利用JSONP解决AJAX跨域问题的原理与jQuery解决方案JSONP jQue...

  • 使用JSONP解决ajax跨域

    在日常开发中,不免遇到跨域的问题。在这里我们介绍使用Jsonp来解决ajax跨域的问题 什么是跨域? 跨域,指的是...

  • window.postMessage解决前端ajax跨域问题

    postMessage畅快解决跨域问题 本文主要是记录使用window.postMessage解决ajax跨域问题...

  • SpringMVC 进行ajax跨域请求访问

    关于 springmvc 3.x 版本对ajax跨域请求访问 ajax 请求后,浏览器出现跨域的问题那么在当前环境...

  • Flask-cors跨域

    什么是跨域 为什么要考虑跨域问题 同源策略 解决跨域问题 方式一: 使用 JSONP (一种非Ajax技术,需要前...

  • 解决跨域问题

    概述 在浏览器端进行 Ajax 请求时会出现跨域问题,那么什么是跨域,如何解决跨域呢?先看浏览器端出现跨域问题的现...

  • 跨域问题

    概述 在浏览器端进行 Ajax 请求时会出现跨域问题,那么什么是跨域,如何解决跨域呢?先看浏览器端出现跨域问题的现...

网友评论

      本文标题:Ajax的跨域问题

      本文链接:https://www.haomeiwen.com/subject/pbbhoctx.html