初始ajax和跨域

来源:互联网 发布:淘宝店铺关注怎么刷 编辑:程序博客网 时间:2024/05/18 03:37

AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术,它是一种从页面向服务器请求数据的技术。

AJAX = 异步+js+xml

XMLHttpRequest 对象

创建该对象

var xmlhttp;if (window.XMLHttpRequest)  {// code for IE7+, Firefox, Chrome, Opera, Safari  xmlhttp=new XMLHttpRequest();  }else  {// code for IE6, IE5  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");  }

常用方法和属性

方法(属性) 说明 open(method,url,async) 规定请求的类型、URL 以及是否异步处理请求。 method:请求的类型;GET 或 POST url:文件在服务器上的位置- async:true(异步)或 false(同步) send(string) 将请求发送到服务器。string:仅用于 POST 请求 abort() 终止请求 setRequestHeader(header,value) 向请求添加 HTTP 头。在调用open()之后send()之前调用。header: 规定头的名称 value: 规定头的值 getRequestHeader(header) 取得响应的相应头部信息getAllRequestHeaders()取得一个包含所有头部信息的长字符串 onreadyStatechange() 每当 readyState 属性改变时,就会调用该函数。 responseText 作为响应主体被返回的文本 responseXML 若响应的内容类型为text/xml或者application/xml,则该属性包含响应数据的XML DOM文档 status http状态码 statusText http状态说明 readyState 请求/响应过程的当前活动阶段(0,1,2,3,4)对应有onreadystatechange事件处理程序,该程序必须在XHR调用open()之前指定

接收响应后,先检查status属性,确定响应已经返回后再进行其他操作。

get 最常用于向服务器查询某些信息。注意:传入open方法中的url末尾的字符串必须经过正确的编码,构建一个编码函数。

function addURLParam(url,name,value){      url += (url.indexOf("?") == -1 > "?" : "&";      url += encodeURIComponent(name) + "=" +encodeURIComponent(value);      return url;}

post 通常向服务器发送应该保存的数据。当send XML DOM文档时,传入的文档要经过序列化。

jquery中有一个ajax方法,可以很方便的实现ajax。
下面我们看一下利用jquery实现ajax的代码

<body><h1>员工查询</h1><input type="text" id="userid" /><button id="search">查询</button><p id="result"></p><h2>创建员工</h2><label>请输入员工姓名:</label><input type="text" id="username" /><br/><label>请输入员工编号:</label><input type="text" id="user_id" /><br/><label>请选择员工性别:</label><select id="usersex"><option></option><option></option></select><br/><label>请选择员工职位:</label><input type="text" id="userjob" /><br/><button id="save">保存</button><p id="create"></p><!-- 引入jquery库 --><script src="https://code.jquery.com/jquery-3.1.1.min.js"></script><script>/*给查询按钮和保存按钮添加响应函数,实现查询和添加员工功能*///jquery初始化$(document).ready(function(){    $("#search").click(function(){        $.ajax({            type:"GET",   //type:请求的类型,GET,POST            url:"http://121.195.170.175:8077/ajaxdemo/serverjquery.php?number="+$("#userid").val(),              dataType:"json",  //预期服务器返回的数据类型,不指定的话,jQuery自动根据http包中的MIME信息来判断,一般我们采用json格式            success: function(data){                if(data.success){                    $("#result").html(data.msg);                   }                else{                    $("#result").html("fault happened " + data.msg);                }            },   //一个方法,请求成功后的回调函数。传入返回后的数据以及包含成功代码的字符串。            error:function(jqXHR){                $("#result").html("fault happened " + jqXHR.status);            },   //一个方法,请求失败时调用此函数。传入XHR对象        });    });    $("#save").click(function(){        $.ajax({            type:"POST",            url:"serverjson.php",            dataType:"json",            data:{                name:$("#username").val(),                number:$("#user_id").val(),                sex:$("#usersex").val(),                job:$("#userjob").val()            },   //数据,连同请求发送到服务器中,主要用于POST            success: function(data){                if(data.success){                    $("#create").html(data.msg);                   }                else{                    $("#create").html("fault happened " + data.msg);                }            },            error:function(jqXHR){                $("#create").html("fault happened " + jqXHR.status);            },        });    });});</script></body>

XHR2

下面介绍XHR2新定义的几个特性。

FormData

XHR2定义了FormData类型来封装表单数据的序列化。

var data = new FormData();data.append("name","Nicholas");

这段代码创建了一个FormData对象,并向其中添加了一些数据,append()方法接受2个参数,分别对应表单字段的名字和字段包含的值。
我们还可以直接向FormData构造函数传入表单元素。

var data = new FormData(document.forms[0]);

还可以直接将FormData对象实例传给send(),这样就可以不必明确地在XHR对象上设置请求头部,因为XHR对象可以识别传入的数据是FormData的实例,相应地配置适当的头部信息。

xhr.open("post","server.php");var form = document.getElementById("user-info");xhr.send(new FormData(form));

超时限定

XHR对象有一个timeout属性,表示请求在等待响应多少毫秒之后就终止。在send之前,我们可以给timeout赋值,如果在规定的时间之内,还未有响应返回,就会触发timeout事件,调用ontimeout事件处理程序。

var xhr = createXHR();xhr.onreadystatechange = function(){    if(xhr.readyState == 4){        try{           //处理代码        }        catch(ex){        //假设由ontimeout事件处理程序处理        }    }};xhr.open("get","server.php");xhr.timeout = 1000;     //1s(IE8+)xhr.ontimeout = function(){    alert("request did not return in a second!");}xhr.send();

但是有一个小问题,就是说如果在1s时,请求还没有返回,就会自动终止请求,同时会调用ontimeout事件处理程序,但此时有可能readyState属性值已经变成了4,就会调用onreadystatechange事件处理程序,可是,如果在终止请求后访问status属性就会报错。所以我们通常将检查status属性的代码封装到一个try-catch块内。

overrideMimeType()

该方法用于重写XHR响应的MIME类型。

进度事件

总共6个进度事件。

事件名 说明 loadstart 在接受到响应数据的第一个字节时触发 progress 接收响应期间不断触发 error 请求发生错误时触发 abort 在因为调用abort()方法而终止连接时触发 load 接收到完整的响应数据时触发 loadend 通信完成或者触发error,abort,load事件后触发
**每个请求都从触发loadstart事件开始,接着是一个或多个progress事件,然后触发error,abort,load事件中的一个,最后以触发loadend事件结束**。

提一下progress事件。onprogress事件处理程序会接收到一个event对象,其target属性是对应的XHR对象,但是包含着3个额外的属性,lengthComputable,position,totalSize。其中lengthComputable是一个表示进度信息是否可用的布尔值,position表示已经接收到的字节数,totalSize表示根据Content-length响应头部确定的预期字节数。
根据上面这些,我们可以为一个用户创建一个进度指示器。

xhr.onprogress = function(event){    var divStatus = document.getElementById("status");    if(event.lengthComputable){        divStatus.innerHTML = "Received " + event.position + " of " + event.totalSize + " bytes";    }};

跨域

不同域之间相互请求资源叫做跨域。

http://[协议]www[子域名].abc.com[主域名]:8080[端口号]/scipts/jquery.js[请求资源地址]
当协议,子域名,主域名,端口号任意一个不同时,都算作不同的域。
域名和域名对应ip间通信同样属于跨域。

代理

主要是通过后台实现,服务器改造,客户端代码不变,不属于前端范畴,在这里只提一下

jsonp

JSON with padding 填充式JSON或者参数式JSON
主要是利用在页面中创建script节点的方法向不同域提交HTTP请求的方法。
原理:script标签的src属性是没有跨域的限制的。
是被包含在函数调用中的JSON。由两部分组成:
jsonp由两部分组成,回调函数和数据。
  - 回调函数:当响应到来时应该在页面中调用的函数。一般在请求中指定
  - 数据:传入回调函数中的JSON数据

今天上午无意中看到一篇文章,叫说说JSON和JSONP,也许你会豁然开朗,觉得里面关于jsonp的那部分说得特别好,关于这个技术的产生和原理都说的很清晰,所以在这里特意来给自己的文章添上几笔。
首先要郑重声明:

jsonp不是ajax的一个特例。

jsonp的产生

  • Ajax直接请求普通文件存在跨域无权限访问的问题;
  • Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如script、img、iframe)
  • 于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;
  • 恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;
  • 这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。
  • 客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。
  • 为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

jsonp客户端的具体实现过程
 1、我们知道,哪怕跨域js文件中的代码(当然指符合web脚本安全策略的),web页面也是可以无条件执行的。
  远程服务器remoteserver.com根目录下有个remote.js文件代码如下:

alert('我是远程文件');

本地服务器localserver.com下有个jsonp.html页面代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>    <title></title>    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script></head><body></body></html>

毫无疑问,页面将会弹出一个提示窗体,显示跨域调用成功。
  2、现在我们在jsonp.html页面定义一个函数,然后在远程remote.js中传入数据进行调用。
  jsonp.html页面代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>    <title></title>    <script type="text/javascript">    var localHandler = function(data){        alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);    };    </script>    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script></head><body></body></html>

remote.js文件代码如下:

localHandler({"result":"我是远程js带来的数据"});

运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程js调用成功,并且还接收到了远程js带来的数据。很欣喜,跨域远程获取数据的目的基本实现了,但是又一个问题出现了,我怎么让远程js知道它应该调用的本地函数叫什么名字呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?我们接着往下看。
  3、聪明的开发者很容易想到,只要服务端提供的js脚本是动态生成的就行了呗,这样调用者可以传一个参数过去告诉服务端“我想要一段调用XXX函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。
  看jsonp.html页面的代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>    <title></title>    <script type="text/javascript">    // 得到航班信息查询结果后的回调函数    var flightHandler = function(data){        alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');    };    // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)    var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";    // 创建script标签,设置其属性    var script = document.createElement('script');    script.setAttribute('src', url);    // 把script标签加入head,此时调用开始    document.getElementsByTagName('head')[0].appendChild(script);    </script></head><body></body></html>

这次的代码变化比较大,不再直接把远程js文件写死,而是编码实现动态查询,而这也正是jsonp客户端实现的核心部分,本例中的重点也就在于如何完成jsonp调用的全过程。
  我们看到调用的url中传递了一个code参数,告诉服务器我要查的是CA1998次航班的信息,而callback参数则告诉服务器,我的本地回调函数叫做flightHandler,所以请把查询结果传入这个函数中进行调用。
  OK,服务器很聪明,这个叫做flightResult.aspx的页面生成了一段这样的代码提供给jsonp.html(服务端的实现这里就不演示了,与你选用的语言无关,说到底就是拼接字符串):

flightHandler({    "code": "CA1998",    "price": 1780,    "tickets": 5});

我们看到,传递给flightHandler函数的是一个json,它描述了航班的基本信息。运行一下页面,成功弹出提示窗口,jsonp的执行全过程顺利完成!
  4、到这里为止的话,相信你已经能够理解jsonp的客户端实现原理了吧?剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用。

先前的例子:

function handleResponse(response){    alert("you're at ip address " + response.ip + ", which is in " + response.city + ", " + response.region_name);}var script = document.createElement("script");script.src = "http://freegeoip.net/json/?callback=handleResponse";document.body.insertBefore(script,document.body.firstChild);

jsonp的优势是能够直接访问响应文本,支持在浏览器和服务器之间的双向通信。不足的地方是jsonp方式只支持GET请求,不支持POST请求。

下面我们看一下在Jquery中用jsonp的代码,仍旧是上面那个例子

<body><!-- 和上面一样 --><script src="https://code.jquery.com/jquery-3.1.1.min.js"></script><script>$(document).ready(function(){    $("#search").click(function(){        $.ajax({            type:"GET",            url:"http://121.195.170.175:8077/ajaxdemo/serverjsonp.php?number="+$("#userid").val(),            dataType:"jsonp",   //改动一            jsonp:"callback",    //改动二            success: function(data){                if(data.success){                    $("#result").html(data.msg);                   }                else{                    $("#result").html("fault happened " + data.msg);                }            },            error:function(jqXHR){                $("#result").html("fault happened " + jqXHR.status);            },        });    });    //save 点击响应部分});</script></body>

这只是前端部分的代码,后台根据你使用的后台语言做出相应的改动。
所以说jsonp是需要服务器端的页面进行相应的配合的。

CORS

CORS的基本思想是:使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是应该失败。
实现此功能非常简单,只需由服务器发送一个响应标头即可。
在服务器做一个简单的改动,这是在php中的代码

header('Access-Control-Allow-Origin:*'); /指定跨域的域名

设置之后我们可以在调试工具中查看header是否已经设置成功。
但是CORS要求服务器首先要具备对CORS支持,这样浏览器才能对该服务器进行跨源访问。
这里要提一下,IE中引入XDR(XDomainRequest)来实现跨域安全通信,其余浏览器仍然是XHR对象。随着html5概念的出现,在原有XHR对象上提出了XHR2,XHR2中很好地支持了CORS。
下面是XDR和XHR的几个不同点:
- cookie不会随请求发送,也不会随响应返回。
- 只能设置请求头部信息中的Content-Type字段
- 不能访问响应头部信息
- 只支持GET和POST请求

### document.domain跨子域

只适用于不同子域的框架间的交互。

不同的框架之间是可以获取window对象的,但却无法获取相应的属性和方法。比如,有一个页面,它的地址是http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的。

<script type="text/javascript">    function test(){        var iframe = document.getElementById('ifame');        var win = document.contentWindow;//可以获取到iframe里的window对象,但该window对象的属性和方法几乎是不可用的        var doc = win.document;//这里获取不到iframe里的document对象        var name = win.name;//这里同样获取不到window对象的name属性    }</script><iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>

这个时候,document.domain就可以派上用场了,我们只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。

1.在页面 http://www.example.com/a.html 中设置document.domain:

<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe><script type="text/javascript">    document.domain = 'example.com';//设置成主域    function test(){        alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象    }</script>

2.在页面 http://example.com/b.html 中也设置document.domain:

<script type="text/javascript">    document.domain = 'example.com';//在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同</script>

使用html5的新方法 window.postMessage()

postMessage(data,origin)接受两个参数,data为要传递的数据,origin为目标窗口的源,包括协议+主机+端口([url]),其中url会被忽略,所以可以不写。

Comet

与Ajax不同,Comet是一种服务器向页面推送数据的技术。它能使信息近乎实时地被推送到页面上。
有两种实现Comet的方式:长轮询和流。
长轮询是短轮询的一个翻版。长轮询是页面发起一个到服务器的请求,然后服务器一直保持打开,直到有数据可以发送。发送完数据后浏览器关闭连接,随即又发送一个新的请求。这一过程在页面打开期间一直持续不断。
HTTP流不同于轮询,它在页面的整个生命周期内只使用一个HTTP连接。具体来说,就是浏览器向服务器发送一个请求,而服务器保持连接打开,然后周期性地向浏览器发送数据。

服务器发送事件SSE

SSE【Server-Sent Events】是围绕只读Comet交互推出的API或者模式。
创建到服务器的单向连接,服务器通过这个连接可发送任意数量的数据。服务器响应的类型不许是text/event-stream。SSE支持短轮询,长轮询和HTTP流,能在断开连接时自动确定何时重新连接。

var source = new EventSource("server.php");source.onmessage=function(event){  document.getElementById("result").innerHTML+=event.data + "<br />"; }; source.close();

Web Sockets

html5中的新规范。它可以在一个单独的连接上提供全双工,双向的通信。
直接看代码。

var socket = new WebSocket("ws://www.example.com/server.php"); //必须给构造函数传入绝对url,websockets不适用同源策略。socket.send("hello world!");socket.onmessage = function(event){    var data = event.data;    //处理数据};socket.onopen = function(){    alert("connection established!");};socket.onerror = function(){    alert("connection error!");};socket.onclose = function(){    alert("connection close!");};socket.close(); //关闭连接

上面那段代码基本上涵盖了有关websocket的常用的方法。
下面我们说说其中的一些细节。
- websocket只能通过连接发送纯文本数据,如代码中的Hello world!字符串,因此,对于复杂的数据结构,在通过连接发送之前,必须经过序列化。此时,json又派上了大用场。我们可以利用将数据序列化为一个Json字符串,再send。与send一样,onmessage中的event对象有一个data属性,也是字符串,如果想得到其他格式的数据,需要解析他们。
- 代码中的几个事件,只有close事件的event对象有额外的信息。该event有三个额外的属性:wasClean,code,reason。wasClean是一个布尔值,表示连接是否已经明确关闭,code是服务器返回的数值状态码,而reason是一个字符串,包含服务器返回的信息。我们可以读取这些信息反馈给用户看。

关于跨域,还在学习中,如果上面我所说的有错误,欢迎指正。

1 0
原创粉丝点击