详解自定义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 相关文章推荐
JavaScript读取中文cookie时的乱码问题的解决方法
Oct 14 Javascript
js获取键盘按键响应事件(兼容各浏览器)
May 16 Javascript
JS代码同步文本框内容的实例方法
Jul 12 Javascript
js中判断变量类型函数typeof的用法总结
Aug 09 Javascript
JS防止网页被嵌入iframe框架的方法分析
Sep 13 Javascript
AngularJS+Bootstrap实现多文件上传与管理
Nov 08 Javascript
Javascript中字符串replace方法的第二个参数探究
Dec 05 Javascript
用纯Node.JS弹出Windows系统消息提示框实例(MessageBox)
May 17 Javascript
js实现导航跟随效果
Nov 17 Javascript
关于微信小程序获取小程序码并接受buffer流保存为图片的方法
Jun 07 Javascript
layui 关闭open弹出框 刷新table表格页面的方法
Sep 16 Javascript
ant design实现圈选功能
Dec 17 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 cdata 处理(详细介绍)
2013/07/05 PHP
ThinkPHP3.1新特性之多层MVC的支持
2014/06/19 PHP
PHP获取数组中重复最多的元素的实现方法
2014/11/11 PHP
php获取ip及网址的简单方法(必看)
2017/04/01 PHP
jQuery EasyUI API 中文文档 - ComboTree组合树
2011/10/11 Javascript
jQuery源码分析-04 选择器-Sizzle-工作原理分析
2011/11/14 Javascript
javascript 3d 逐侦产品展示(核心精简)
2014/03/26 Javascript
jQuery层动画定位滑动效果的方法
2015/04/30 Javascript
使用AngularJS实现可伸缩的页面切换的方法
2015/06/19 Javascript
JavaScript仿商城实现图片广告轮播实例代码
2016/02/06 Javascript
javascript数组常用方法汇总
2016/09/10 Javascript
webpack学习笔记之优化缓存、合并、懒加载
2017/08/24 Javascript
React操作真实DOM实现动态吸底部的示例
2017/10/23 Javascript
layui radio性别单选框赋值方法
2018/08/15 Javascript
nodejs初始化init的示例代码
2018/10/10 NodeJs
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
2018/11/12 Javascript
jQuery AJAX与jQuery事件的分析讲解
2019/02/18 jQuery
[00:31]2016完美“圣”典风云人物:国士无双宣传片
2016/12/04 DOTA
Python 高级专用类方法的实例详解
2017/09/11 Python
Python网络爬虫之爬取微博热搜
2019/04/18 Python
django和vue实现数据交互的方法
2019/08/21 Python
python如何通过twisted搭建socket服务
2020/02/03 Python
Gloeilampgoedkoop荷兰:在线购买灯泡
2019/02/16 全球购物
FragranceNet中文网:北美健康美容线上零售商
2020/08/26 全球购物
请解释在new与override的区别
2012/10/29 面试题
什么是属性访问器
2015/10/26 面试题
How to spawning asynchronous work in J2EE
2016/08/29 面试题
一个大学生十年的职业规划
2014/01/17 职场文书
舞蹈比赛获奖感言
2014/02/04 职场文书
前处理组长岗位职责
2014/03/01 职场文书
大学生党员自我评价范文
2014/04/09 职场文书
英语求职信范文
2014/05/23 职场文书
2015最新婚礼主持词
2015/06/30 职场文书
《风筝》教学反思
2016/02/23 职场文书
2019下半年英语教师的教学工作计划(3篇)
2019/09/25 职场文书
Java面试题冲刺第十六天--消息队列
2021/08/07 面试题