详解自定义ajax支持跨域组件封装


Posted in Javascript onFebruary 08, 2018

Class.create()分析

仿prototype创建类继承

var Class = {
  create: function () {
    var c = function () {
      this.request.apply(this, arguments);
    }
    for (var i = 0, il = arguments.length, it; i < il; i++) {
      it = arguments[i];
      if (it == null) continue;
      Object.extend(c.prototype, it);
    }
    return c;
  }
};
Object.extend = function (tObj, sObj) { 
  for (var o in sObj) {
    tObj[o] = sObj[o];
  }
  return tObj;
};

ajax定义:ZIP_Ajax=Class.create();

其中create方法返回的是一个构造函数request,这里相当于var ZIP_Ajax= function(){ this.request.apply(this, arguments); }; 用对象冒充的方式在函数内部执行了一次构造的过程,相当于把构造函数任务交给了request方法,这里的this.request是ZIP_Ajax的实例的方法,而this指向的就是ZIP_Ajax的实例,apply后面的this指向的是ZIP_Ajax,最后根据new关键字将this才真正指向ZIP_Ajax类。有了类ZIP_Ajax的定义,接下来就可以定义其方法:

XMLHttpRequest详解:

XMLHttpRequest不是一项技术而是一个内置于主流浏览器内的一个可以完全访问http协议的对象。传统的http请求大部分都是基于form提交请求http,然后返回一个form。XMLHttpRequest支持同步请求的同时最大的优点就是支持异步传输接受数据,新建一个ajax请求实际就是实例化一个XMLHttpRequest对象。简单介绍一下主要事件及方法:

readystatechange事件:

当XMLHttpRequest发送一个http请求之后就会激发一个readystatechange事件,事件返回有五个值,0,1,2分别代表创建XMLHttpRequest、初始化完成XMLHttpRequest、发送了请求,3代表响应没结束(即只接收到响应头数据)4才是真正获得完整响应。

返回的status状态表示服务器返回的状态码:

常用的有200表示成功返回数据,301永久重定向,302为临时重定向(不安全)304读取的缓存数据400表示请求出现语法错误,403表示服务器拒绝请求,404表示请求网页资源不存在,405找不到指定位置服务器,408表示请求超时,500服务器内部错误,505表示服务器不支持请求的http协议版本。

200-300表示成功,300-400表示重定向,400-500表示请求内容或者格式或者请求体过大导致错误,500+表示服务器内部错误

open方法:

open接收三个参数分别是请求类型(get,post,head等)、url、同步或者异步

send方法:

当请求就绪后会触发send方法,发送的内容就是请求的数据(如果是get请求则参数为null;

请求成功之后会执行success自定义方法,其参数为返回数据;

ajax跨域:

什么是跨域?

如果两个站点www.a.com想向www.b.com请求数据就出现了因为域名不一致导致的跨域问题。即使是域名相同,如果端口不同的话也是会存在跨域问题(这种原因js是只能袖手旁观了)。判断是否跨域仅仅是通过window.location.protocol+window.location.host来判断例如http://www.baidu.com.

js解决跨域问题几种方案?

1、document.domain+iframe

对于主域相同而子域不同的请求可以使用域名+iframe作为解决办法。具体思想是假如有两个域名下的不同ab文件www.a.com/a.html

以及hi.a.com/b.html,我们可以在两个html文件中加上document.domain="a.com",之后通过在a文件中创建一个iframe去控制iframe的contentDocument,这样两个文件就可以对话了。举例如下:

www.a.com上的a.html文件中

document.domain="a.com";
  var selfFrame=document.createElement("iframe");
  selfFrame.src="http://hi.a.com/b.html";
  selfFrame.style.display="none";
  document.body.appendChild(selfFrame);
  selfFrame.onload=function(){
    var doc=selfFrame.contentDocument||selfFrame.contentWindow.document;//得到操作b.html权限
    alert(doc.getElementById("ok_b").innerHTML());//具体操作b文件中元素
  }

hi.a.com上的b.html文件中

document.domain="a.com";

问题:

1、安全性,当一个站点(hi.a.com)被攻击后,另一个站点(www.a.com)会引起安全漏洞。2、如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。

2、动态创建script(传说中jsonp方式)

浏览器默认禁止跨域访问,但不禁止在页面中引用其他域名的js文件,并且可以执行引入js文件中的方法等,根据这点我们可以通过创建script节点方法来实现完全跨域的通信。实现步骤为:

a.在请求发起方页面动态加载一个script,script的url指向接收方的后台,该地址返回的javascript方法会被发起方执行,url可以传参并仅支持get提交参数。

b.加载script脚本时候调用跨域的js方法进行回调处理(jsonp)。

举例如下:

发起方

function uploadScript(options){
  var head=document.getElementsByTagName("head")[0];
  var script=document.createElement("script");
  script.type="text/javasctipt";
  options.src += '?callback=' + options.callback;
  script.src=options.src;
  head.insertBefore(script,head.firstChild);
}
function callback(data){}
window.onload=function(){//调用
  uploadScript({src:"http://e.com/xxx/main.ashx",callback:callback})
}

接收方:

接收方只需要返回一个执行函数,该执行函数就是请求中的callback并赋参数。

3、使用html5的postMessage:

html5新功能有一个就是跨文档消息传输,如今大部分浏览器都已经支持并使用(包括ie8+),其支持基于web的实时消息传递并且不存在跨域问题。postMessage一般会跟iframe一起使用。

举例如下:

父页面:

<iframe id="myPost" src="http//www.a.com/main.html"></iframe>
window.onload=function(){
  document.getElementById("myPost").contentWindow.postMessage("显示我","http://www.a.com")
  //第二个参数表示确保数据发送给适合域名的文档
}
a.com/main.html页面:
window.addEventListener("message",function(event){
  if(event.origin.indexOf("a.com")>-1){
    document.getElementById("textArea").innerHTML=event.data;
  }
},false)
<body>
  <div>
    <span id="textArea"></span>
  </div>
</body>

这样在父页面加载完成后main.html页面的textArea部分就会显示"显示我"三个字

ajax方法封装code:

ZIP_Ajax.prototype={
  request:function(url options){
    this.options=options;
    if(options.method=="jsonp"){//跨域请求
      return this.jsonp();
    }
    var httpRequest=this.http();
    options=Object.extend({method: 'get',
      async: true},options||{});
    
    if(options.method=="get"){
      url+=(url.indexOf('?')==-1?'?':'&')+options.data;
      options.data=null;
    }
    httpRequest.open(options.method,url,options.async);
    if (options.method == 'post') {
      httpRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=UTF-8');
    }
    httpRequest.onreadystatechange = this._onStateChange.bind(this, httpRequest, url, options);
    httpRequest.send(options.data || null);//get请求情况下data为null
    return httpRequest;
  },
  jsonp:function(){
    jsonp_str = 'jsonp_' + new Date().getTime();
    eval(jsonp_str + ' = ' + this.options.callback + ';');    
    this.options.url += '?callback=' + jsonp_str;
    for(var i in this.options.data) {
      this.options.url += '&' + i + '=' + this.options.data[i];
    } 
    var doc_head = document.getElementsByTagName("head")[0],
      doc_js = document.createElement("script"),
      doc_js.src = this.options.url;
    doc_js.onload = doc_js.onreadystatechange = function(){
       if (!this.readyState || this.readyState == "loaded" || this.readyState == "complete"){
         //清除JS
         doc_head.removeChild(doc_js);      
        }
      }   
      doc_head.appendChild(doc_js);
  },
  http:function(){//判断是否支持xmlHttp
    if(window.XMLHttpRequest){
      return new XMLHttpRequest();
    }
    else{
      try{
        return new ActiveXObject('Msxml2.XMLHTTP')
      }
      catch(e){
        try {
          return new ActiveXObject('Microsoft.XMLHTTP');
        } catch (e) {
          return false;
        }
      }
    }
  },
  _onStateChange:function(http,url,options){
    if(http.readyState==4){
      http.onreadystatechange=function(){};//重置事件为空
      var s=http.status;
      if(typeof(s)=='number'&&s>200&&s<300){
        if(typeof(options.success)!='function')return;
        var format=http;
        if(typeof(options.format)=='string'){//判断请求数据格式
          switch(options.format){
            case 'text':
              format=http.responseText;
              break;
            case 'json':
              try{
                format=eval('('+http.responseText+')');
              }
              catch (e) {
                if (window.console && console.error) console.error(e);
              }
              break;
            case 'xml':
              format=http.responseXML;
              break;
          }
        }
      options.success(format);//成功回调
      }
      else {//请求出问题后处理
        if (window.closed) return;
        if (typeof (options.failure) == 'function') {
          var error = {
            status: http.status,
            statusText: http.statusText
          }
          //  判断是否是网络断线或者根本就请求不到服务器
          if (http.readyState == 4 && (http.status == 0 || http.status == 12030)) {
            //  是
            error.status = -1;
          }
          options.failure(error);
        }
      }
    } 
  }
};

使用方法:

ajax调用举例:

var myAjax=new ZIP_Ajax("http://www.a.com/you.php",{
  method:"get",
  data:"key=123456&name=yuchao",
  format:"json",
  success:function(data){
    ......
  }
})
跨域请求调用举例:
var jsonp=new ZIP_Ajax("http://www.a.com/you.php",{
  method:"jsonp",
  data:{key:"123456",name:"yuchao"},
  callback:function(data){
    ......
  }
})

 

Javascript 相关文章推荐
top.location.href 没有权限 解决方法
Aug 05 Javascript
另一个javascript小测验(代码集合)
Jul 27 Javascript
网页右侧悬浮滚动在线qq客服代码示例
Apr 28 Javascript
AngularJS学习笔记之TodoMVC的分析
Feb 22 Javascript
JavaScript实战之菜单特效
Aug 16 Javascript
微信小程序 标签传入数据
May 08 Javascript
JavaScript requestAnimationFrame动画详解
Sep 14 Javascript
微信小程序实现图片上传功能
May 28 Javascript
详解.vue文件中style标签的几个标识符
Jul 17 Javascript
使用Sonarqube扫描Javascript代码的示例
Dec 26 Javascript
Vue分页效果与购物车功能
Dec 13 Javascript
el-table表头根据内容自适应完美解决表头错位和固定列错位
Jan 07 Javascript
微信小程序实现验证码获取倒计时效果
Feb 08 #Javascript
ES6 迭代器(Iterator)和 for.of循环使用方法学习(总结)
Feb 08 #Javascript
详解 vue better-scroll滚动插件排坑
Feb 08 #Javascript
VUE + UEditor 单图片跨域上传功能的实现方法
Feb 08 #Javascript
关于vue中watch检测到不到对象属性的变化的解决方法
Feb 08 #Javascript
使用veloticy-ui生成文字动画效果
Feb 08 #Javascript
js中let和var定义变量的区别
Feb 08 #Javascript
You might like
支持中文的php加密解密类代码
2011/11/27 PHP
php发送get、post请求的6种方法简明总结
2014/07/08 PHP
php去掉URL网址中带有PHPSESSID的配置方法
2014/07/08 PHP
如何使用php实现评委评分器
2015/07/31 PHP
Yii多表联合查询操作详解
2016/06/02 PHP
IFrame跨域高度自适应实现代码
2012/08/16 Javascript
javascript用函数实现对象的方法
2015/05/14 Javascript
JavaScript使用addEventListener添加事件监听用法实例
2015/06/01 Javascript
javascript显示中文日期的方法
2015/06/18 Javascript
javascript随机显示背景图片的方法
2015/06/18 Javascript
jQuery动态背景图片效果实现方法
2015/07/03 Javascript
浅析jquery与checkbox的checked属性的问题
2016/04/27 Javascript
JSONP和批量操作功能的实现方法
2016/08/21 Javascript
angular和BootStrap3实现购物车功能
2017/01/25 Javascript
原生js获取left值和top值的三种方法
2017/08/02 Javascript
Node.js模块全局安装路径配置方法
2018/05/17 Javascript
ES6基础之解构赋值(destructuring assignment)
2019/02/21 Javascript
微信小程序开发常见问题及解决方案
2019/07/11 Javascript
bootstrap table插件动态加载表头
2019/07/19 Javascript
微信小程序授权登陆及每次检查是否授权实例代码
2019/09/18 Javascript
基于Vant UI框架实现时间段选择器
2020/12/24 Javascript
[03:44]2014DOTA2国际邀请赛 71专访:DK战队赛前讨论视频遭泄露
2014/07/13 DOTA
Python中的异常处理学习笔记
2015/01/28 Python
python实现连接mongodb的方法
2015/05/08 Python
Python全局变量用法实例分析
2016/07/19 Python
VSCode下好用的Python插件及配置
2018/04/06 Python
在 Python 中接管键盘中断信号的实现方法
2020/02/04 Python
使用darknet框架的imagenet数据分类预训练操作
2020/07/07 Python
从python读取sql的实例方法
2020/07/21 Python
Python 使用xlwt模块将多行多列数据循环写入excel文档的操作
2020/11/10 Python
2015年入党积极分子评语
2015/03/26 职场文书
入党介绍人意见范文
2015/06/01 职场文书
运动会班级前导词
2015/07/20 职场文书
CSS预处理框架——Stylus
2021/04/21 HTML / CSS
Python实现socket库网络通信套接字
2021/06/04 Python
再次探讨go实现无限 buffer 的 channel方法
2021/06/13 Golang