探讨跨域请求资源的几种方式(总结)


Posted in Javascript onDecember 02, 2016

跨域请求资源的几种方式,具体如下:

1.什么是跨域

2.JSONP

3.proxy代理

4.cors

5.xdr

由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。具体可以查看下表

探讨跨域请求资源的几种方式(总结)

JSONP

这种方式主要是通过动态插入一个script标签。浏览器对script的资源引用没有同源限制,同时资源加载到页面后会立即执行(没有阻塞的情况下)。

<script>
   var _script = document.createElement('script');
   _script.type = "text/javascript";
   _script.src = "http://localhost:8888/jsonp?callback=f";
   document.head.appendChild(_script);
  </script>

实际项目中JSONP通常用来获取json格式数据,这时前后端通常约定一个参数callback,该参数的值,就是处理返回数据的函数名称。

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
  <title>jsonp_test</title>

  <script>
   var f = function(data){
    alert(data.name);
   }
   /*var xhr = new XMLHttpRequest();
   xhr.onload = function(){
    alert(xhr.responseText);
   };
   xhr.open('POST', 'http://localhost:8888/cors', true);
   xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
   xhr.send("f=json");*/
  </script>
  
  <script>
   var _script = document.createElement('script');
   _script.type = "text/javascript";
   _script.src = "http://localhost:8888/jsonp?callback=f";
   document.head.appendChild(_script);
  </script>
 </head>
 var query = _url.query;
    console.log(query);
    var params = qs.parse(query);
    console.log(params);
    var f = "";
  
    f = params.callback;
  
    res.writeHead(200, {"Content-Type": "text/javascript"});
    res.write(f + "({name:'hello world'})");
    res.end();

探讨跨域请求资源的几种方式(总结)

缺点:

1、这种方式无法发送post请求(这里)

2、另外要确定jsonp的请求是否失败并不容易,大多数框架的实现都是结合超时时间来判定。

Proxy代理

这种方式首先将请求发送给后台服务器,通过服务器来发送请求,然后将请求的结果传递给前端。

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
  <title>proxy_test</title>

  <script>
   var f = function(data){
    alert(data.name);
   }
   var xhr = new XMLHttpRequest();
   xhr.onload = function(){
    alert(xhr.responseText);
   };
   xhr.open('POST', 'http://localhost:8888/proxy?http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer', true);
   xhr.send("f=json");
  </script>
 </head>
 
 <body>
 </body>
</html>
var proxyUrl = "";
   if (req.url.indexOf('?') > -1) {
     proxyUrl = req.url.substr(req.url.indexOf('?') + 1);
     console.log(proxyUrl);
   }
   if (req.method === 'GET') {
     request.get(proxyUrl).pipe(res);
   } else if (req.method === 'POST') {
     var post = '';   //定义了一个post变量,用于暂存请求体的信息

    req.on('data', function(chunk){  //通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
      post += chunk;
    });
  
    req.on('end', function(){  //在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
      post = qs.parse(post);
      request({
           method: 'POST',
           url: proxyUrl,
           form: post
         }).pipe(res);
    });
   }

探讨跨域请求资源的几种方式(总结)

需要注意的是如果你代理的是https协议的请求,那么你的proxy首先需要信任该证书(尤其是自定义证书)或者忽略证书检查,否则你的请求无法成功。12306就提供了一个鲜活的例子。

探讨跨域请求资源的几种方式(总结)

探讨跨域请求资源的几种方式(总结)

还需要注意一点,对于同一请求浏览器通常会从缓存中读取数据,我们有时候不想从缓存中读取,所以会加一个preventCache参数,这个时候请求url变成:url?preventCache=12345567....;这本身没有什么问题,问题出在当使用某些前端框架(比如jquery)发送proxy代理请求时,请求url为proxy?url,同时设置preventCache:true,框架不能正确处理这个参数,结果发出去的请求变成proxy?url&preventCache=123456(正长应为proxy?url?preventCache=12356);后端截取后发送的请求为url&preventCache=123456,根本没有这个地址,所以你得不到正确结果。

CORS

这是现代浏览器支持跨域资源请求的一种方式。

探讨跨域请求资源的几种方式(总结)

当你使用XMLHttpRequest发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin;浏览器判断该相应头中是否包含Origin的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
  <title>jsonp_test</title>

  <script>
   /*var f = function(data){
    alert(data.name);
   }*/
   var xhr = new XMLHttpRequest();
   xhr.onload = function(){
    alert(xhr.responseText);
   };
   xhr.open('POST', 'http://localhost:8888/cors', true);
   xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
   xhr.send("f=json");
  </script>
  
  <script>
   /* var _script = document.createElement('script');
   _script.type = "text/javascript";
   _script.src = "http://localhost:8888/jsonp?callback=f";
   document.head.appendChild(_script);*/
  </script>
 </head>
 
 <body>
 </body>
</html>

前端cors

if (req.headers.origin) {

      res.writeHead(200, {
        "Content-Type": "text/html; charset=UTF-8",
        "Access-Control-Allow-Origin":'http://localhost'/*,
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
        'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type'*/
      });
      res.write('cors');
      res.end();
    }

探讨跨域请求资源的几种方式(总结)

如果我们把Access-Control-Allow-Origin去掉,浏览器会驳回响应,我们也就拿不到数据。

探讨跨域请求资源的几种方式(总结)

需要注意的一点是Preflighted Request的透明服务器验证机制支持开发人员使用自定义的头部、GET或POST之外的方法,以及不同类型的主题内容。总结如如:

1、非GET 、POST请求

2、POST请求的content-type不是常规的三个:application/x- www-form-urlencoded(使用 HTTP 的 POST 方法提交的表单)、multipart/form-data(同上,但主要用于表单提交时伴随文件上传的场合)、text/plain(纯文本)

3、POST请求的payload为text/html

4、设置自定义头部

OPTIONS请求头部中会包含以下头部:Origin、Access-Control-Request-Method、Access-Control-Request-Headers,发送这个请求后,服务器可以设置如下头部与浏览器沟通来判断是否允许这个请求。

Access-Control-Allow-Origin、Access-Control-Allow-Method、Access-Control-Allow-Headers

var xhr = new XMLHttpRequest();
   xhr.onload = function(){
    alert(xhr.responseText);
   };
   xhr.open('POST', 'http://localhost:8888/cors', true);
   xhr.setRequestHeader("Content-Type", "text/html");
   xhr.send("f=json");
if (req.headers.origin) {

      res.writeHead(200, {
        "Content-Type": "text/html; charset=UTF-8",
        "Access-Control-Allow-Origin":'http://localhost',
        'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
        'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type'/**/
      });
      res.write('cors');
      res.end();
    }

探讨跨域请求资源的几种方式(总结)

如果你在调试状态,你会发现后台代码执行了两遍,说明发送了两次请求。注意一下我们的onload代码只执行了一次,所以说OPTIONS请求对程序来说是透明的,他的请求结果会被缓存起来。

如果我们修改一下后台代码,把Content-Type去掉,你会发现OPTIONS请求失败。

探讨跨域请求资源的几种方式(总结)

探讨跨域请求资源的几种方式(总结)

通过setRequestHeader('X-Request-With', null)可以避免浏览器发送OPTIONS请求。

根据我的测试,当使用cors发送跨域请求时失败时,后台是接收到了这次请求,后台可能也执行了数据查询操作,只是响应头部不合符要求,浏览器阻断了这次请求。

XDR

这是IE8、IE9提供的一种跨域解决方案,功能较弱只支持get跟post请求,而且对于协议不同的跨域是无能为力的,比如在http协议下发送https请求。看一下微软自己的例子就行

<!DOCTYPE html>

<html>
<body>
 <h2>XDomainRequest</h2>
 <input type="text" id="tbURL" value="http://www.contoso.com/xdr.txt" style="width: 300px"><br>
 <input type="text" id="tbTO" value="10000"><br>
 <input type="button" onclick="mytest()" value="Get">   
  <input type="button" onclick="stopdata()" value="Stop">   
  <input type="button" onclick="readdata()" value="Read">
 <br>
 <div id="dResponse"></div>
 <script>
  var xdr;
  function readdata()
  {
   var dRes = document.getElementById('dResponse');
   dRes.innerText = xdr.responseText;
   alert("Content-type: " + xdr.contentType);
   alert("Length: " + xdr.responseText.length);
  }
  
  function err()
  {
   alert("XDR onerror");
  }

  function timeo()
  {
   alert("XDR ontimeout");
  }

  function loadd()
  {
   alert("XDR onload");
   alert("Got: " + xdr.responseText);
  }

  function progres()
  {
   alert("XDR onprogress");
   alert("Got: " + xdr.responseText);
  }

  function stopdata()
  {
   xdr.abort();
  }

  function mytest()
  {
   var url = document.getElementById('tbURL');
   var timeout = document.getElementById('tbTO');
   if (window.XDomainRequest)
   {
    xdr = new XDomainRequest();
    if (xdr)
    {
     xdr.onerror = err;
     xdr.ontimeout = timeo;
     xdr.onprogress = progres;
     xdr.onload = loadd;
     xdr.timeout = tbTO.value;
     xdr.open("get", tbURL.value);
     xdr.send();
    }
    else
    {
     alert("Failed to create");
    }
   }
   else
   {
    alert("XDR doesn't exist");
   }
  }
 </script>
</body>
</html>

以上就是我在实际项目中遇到的跨域请求资源的情况,有一种跨域需要特别注意就是在https协议下发送https请求,除了使用proxy代理外其他方法都无解,会被浏览器直接block掉。如果哪位道友知道解决方法,麻烦你告诉我一声。

最后附上完整的测试demo

iss中:

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
  <title>jsonp_test</title>

  <script>
   /*var f = function(data){
    alert(data.name);
   }*/
   var xhr = new XMLHttpRequest();
   xhr.onload = function(){
    alert(xhr.responseText);
   };
   xhr.open('POST', 'http://localhost:8888/cors', true);
   xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
   xhr.setRequestHeader("aaaa","b");
   xhr.send("f=json");
  </script>
  
  <script>
   /* var _script = document.createElement('script');
   _script.type = "text/javascript";
   _script.src = "http://localhost:8888/jsonp?callback=f";
   document.head.appendChild(_script);*/
  </script>
 </head>
 
 <body>
 </body>
</html>

node-html

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
  <title>proxy_test</title>

  <script>
   var f = function(data){
    alert(data.name);
   }
   var xhr = new XMLHttpRequest();
   xhr.onload = function(){
    alert(xhr.responseText);
   };
   xhr.open('POST', 'http://localhost:8888/proxy?https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer', true);
   xhr.send("f=json");
  </script>
 </head>
 
 <body>
 </body>
</html>

 

node-server

var http = require('http');
var url = require('url');
var fs = require('fs');
var qs = require('querystring');
var request = require('request');

http.createServer(function(req, res){
  var _url = url.parse(req.url);
  if (_url.pathname === '/jsonp') {
    var query = _url.query;
    console.log(query);
    var params = qs.parse(query);
    console.log(params);
    var f = "";
  
    f = params.callback;
  
    res.writeHead(200, {"Content-Type": "text/javascript"});
    res.write(f + "({name:'hello world'})");
    res.end();
  } else if (_url.pathname === '/proxy') {
   var proxyUrl = "";
   if (req.url.indexOf('?') > -1) {
     proxyUrl = req.url.substr(req.url.indexOf('?') + 1);
     console.log(proxyUrl);
   }
   if (req.method === 'GET') {
     request.get(proxyUrl).pipe(res);
   } else if (req.method === 'POST') {
     var post = '';   //定义了一个post变量,用于暂存请求体的信息

    req.on('data', function(chunk){  //通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
      post += chunk;
    });
  
    req.on('end', function(){  //在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
      post = qs.parse(post);
      request({
           method: 'POST',
           url: proxyUrl,
           form: post
         }).pipe(res);
    });
   }
  } else if (_url.pathname === '/index') {
    fs.readFile('./index.html', function(err, data) {
     res.writeHead(200, {"Content-Type": "text/html; charset=UTF-8"});
      res.write(data);
      res.end();
    });
  } else if (_url.pathname === '/cors') {
    if (req.headers.origin) {

      res.writeHead(200, {
        "Content-Type": "text/html; charset=UTF-8",
        "Access-Control-Allow-Origin":'http://localhost',
        'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
        'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type,aaaa'/**/
      });
      res.write('cors');
      res.end();
    }
  }
  
}).listen(8888);

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

Javascript 相关文章推荐
firefox 和 ie 事件处理的细节,研究,再研究 书写同时兼容ie和ff的事件处理代码
Apr 12 Javascript
JQuery实现用户名无刷新验证的小例子
Mar 22 Javascript
JavaScript中的ArrayBuffer详细介绍
Dec 08 Javascript
javascript实现鼠标移到Image上方时显示文字效果的方法
Aug 07 Javascript
Angularjs 自定义服务的三种方式(推荐)
Aug 02 Javascript
JavaScript、C# URL编码、解码总结
Jan 21 Javascript
js图片延迟加载(Lazyload)三种实现方式
Mar 01 Javascript
vue实现全选、反选功能
Nov 17 Javascript
详解JavaScript中的数组合并方法和对象合并方法
May 11 Javascript
Vue2.0 实现移动端图片上传功能
May 30 Javascript
javaScript实现游戏倒计时功能
Nov 17 Javascript
JavaScript Tab菜单实现过程解析
May 13 Javascript
jQuery实现倒计时(倒计时年月日可自己输入)
Dec 02 #Javascript
JavaScript 计算笛卡尔积实例详解
Dec 02 #Javascript
jQuery联动日历的实例解析
Dec 02 #Javascript
利用jQuery插件imgAreaSelect实现获得选择域的图像信息
Dec 02 #Javascript
利用jQuery插件imgAreaSelect实现图片上传裁剪(同步显示图像位置信息)
Dec 02 #Javascript
有关suggest快速删除后仍然出现下拉列表的bug问题
Dec 02 #Javascript
Vue.js路由组件vue-router使用方法详解
Dec 02 #Javascript
You might like
php抓取网站图片并保存的实现方法
2015/10/29 PHP
PHP编程获取各个时间段具体时间的方法
2017/05/26 PHP
javascript Select标记中options操作方法集合
2008/10/22 Javascript
ScrollDown的基本操作示例
2013/06/09 Javascript
JavaScript去除数组里重复值的方法
2015/07/13 Javascript
js实现有过渡渐变效果的图片轮播相册(兼容IE,ff)
2016/01/19 Javascript
JS闭包、作用域链、垃圾回收、内存泄露相关知识小结
2016/05/16 Javascript
微信小程序 保留小数(toFixed)详细介绍
2016/11/16 Javascript
windows下vue.js开发环境搭建教程
2017/03/20 Javascript
Angular实现较为复杂的表格过滤,删除功能示例
2017/12/23 Javascript
vue中设置、获取、删除cookie的方法
2018/09/21 Javascript
详解nodejs 配置文件处理方案
2019/01/02 NodeJs
微信小程序五子棋游戏的棋盘,重置,对弈实现方法【附demo源码下载】
2019/02/20 Javascript
js判断一个对象是数组(函数)的方法实例
2019/12/19 Javascript
JavaScript 双向链表操作实例分析【创建、增加、查找、删除等】
2020/04/28 Javascript
浅谈javascript事件环微任务和宏任务队列原理
2020/09/12 Javascript
[00:37]DOTA2上海特级锦标赛 OG战队宣传片
2016/03/03 DOTA
Python Queue模块详解
2014/11/30 Python
浅要分析Python程序与C程序的结合使用
2015/04/07 Python
详解Python的Django框架中的模版相关知识
2015/07/15 Python
用python实现的线程池实例代码
2018/01/06 Python
python实现逆序输出一个数字的示例讲解
2018/06/25 Python
Python编程学习之如何判断3个数的大小
2019/08/07 Python
python 将html转换为pdf的几种方法
2020/12/29 Python
美国护肤咨询及美容产品电商:Askderm
2017/02/24 全球购物
Peter Millar官网:美国高档生活服饰品牌
2018/07/02 全球购物
罗兰·穆雷官网:Roland Mouret
2018/09/28 全球购物
西班牙多品牌鞋店连锁店:Krack
2018/11/30 全球购物
学校三八妇女节活动情况总结
2014/03/09 职场文书
生育关怀行动实施方案
2014/03/26 职场文书
纪律教育学习心得体会
2014/09/02 职场文书
群众路线自我剖析及整改措施
2014/11/04 职场文书
检讨书格式范文
2015/05/07 职场文书
因公司原因离职的辞职信范文
2015/05/12 职场文书
pygame面向对象的飞行小鸟实现(Flappy bird)
2021/04/01 Python
DE1103使用报告
2022/04/05 无线电