AJax与Jsonp跨域访问问题小结
####JavaScript的AJax
AJAX即“AsynchronousJavascriptAndXML”(异步JavaScript和XML)
设计AJax使用的一种重要技术是XMLHttpRequest对象。
创建XMLHttpRequest对象的方式:
xmlhttp=newActiveObject("Microsoft.XMLHTTP");//IE浏览器支持的创建方式
xmlhttp=newXMLHTTPRequest();//FireFox,Opera等浏览器支持的创建方式
XMLHttp是一套可以在Javascript、VbScript、Jscript等脚本语言中通过http协议传送或从接收XML及其他数据的一套API。可用于模拟http的GET和POST请求。
可以判断window.XMLHttpRequest对象是否可用然后创建XMLHttpRequest对象。
以下是XMLHttpRequest对象的属性和使用方法,粘贴过来的,注释的很详细。
<html> <head> <title>XMLHTTPRequest对象的说明DEMO</title> <scriptlanguage="javascript"type="text/javascript"> <!-- varxmlhttp; //创建一个XMLHTTPRequest对象 functioncreateXMLHTTPRequext(){ if(window.ActiveXObject){ xmlhttp=newActiveXObject('Microsoft.XMLHTTP'); } elseif(window.XMLHTTPRequest){ xmlhttp=newXMLHTTPRequest(); } } functionPostOrder(xmldoc) { createXMLHTTPRequext(); //方法:open //创建一个新的http请求,并指定此请求的方法、URL以及验证信息 //语法:oXMLHttpRequest.open(bstrMethod,bstrUrl,varAsync,bstrUser,bstrPassword); //参数 //bstrMethod //http方法,例如:POST、GET、PUT及PROPFIND。大小写不敏感。 //bstrUrl //请求的URL地址,可以为绝对地址也可以为相对地址。 //varAsync[可选] //布尔型,指定此请求是否为异步方式,默认为true。如果为真,当状态改变时会调用onreadystatechange属性指定的回调函数。 //bstrUser[可选] //如果服务器需要验证,此处指定用户名,如果未指定,当服务器需要验证时,会弹出验证窗口。 //bstrPassword[可选] //验证信息中的密码部分,如果用户名为空,则此值将被忽略。 //备注:调用此方法后,可以调用send方法向服务器发送数据。 xmlhttp.Open("get","http://localhost/example.htm",false); //varbook=xmlhttp.responseXML.selectSingleNode("//book[@id='bk101']"); //alert(book.xml); //属性:onreadystatechange //onreadystatechange:指定当readyState属性改变时的事件处理句柄 //语法:oXMLHttpRequest.onreadystatechange=funcMyHandler; //如下的例子演示当XMLHTTPRequest对象的readyState属性改变时调用HandleStateChange函数, //当数据接收完毕后(readystate==4)此页面上的一个按钮将被激活 //备注:此属性只写,为W3C文档对象模型的扩展. xmlhttp.onreadystatechange=HandleStateChange; //方法:send //发送请求到http服务器并接收回应 //语法:oXMLHttpRequest.send(varBody); //参数:varBody(欲通过此请求发送的数据。) //备注:此方法的同步或异步方式取决于open方法中的bAsync参数,如果bAsync==False,此方法将会等待请求完成或者超时时才会返回,如果bAsync==True,此方法将立即返回。 //Thismethodtakesoneoptionalparameter,whichistherequestBodytouse.TheacceptableVARIANTinputtypesareBSTR,SAFEARRAYofUI1(unsignedbytes),IDispatchtoanXMLDocumentObjectModel(DOM)object,andIStream*.Youcanuseonlychunkedencoding(forsending)whensendingIStream*inputtypes.ThecomponentautomaticallysetstheContent-LengthheaderforallbutIStream*inputtypes. //如果发送的数据为BSTR,则回应被编码为utf-8,必须在适当位置设置一个包含charset的文档类型头。 //IftheinputtypeisaSAFEARRAYofUI1,theresponseissentasiswithoutadditionalencoding.ThecallermustsetaContent-Typeheaderwiththeappropriatecontenttype. //如果发送的数据为XMLDOMobject,则回应将被编码为在xml文档中声明的编码,如果在xml文档中没有声明编码,则使用默认的UTF-8。 //IftheinputtypeisanIStream*,theresponseissentasiswithoutadditionalencoding.ThecallermustsetaContent-Typeheaderwiththeappropriatecontenttype. xmlhttp.Send(xmldoc); //方法:getAllResponseHeaders //获取响应的所有http头 //语法:strValue=oXMLHttpRequest.getAllResponseHeaders(); //备注:每个http头名称和值用冒号分割,并以\r\n结束。当send方法完成后才可调用该方法。 alert(xmlhttp.getAllResponseHeaders()); //方法:getResponseHeader //从响应信息中获取指定的http头 //语法:strValue=oXMLHttpRequest.getResponseHeader(bstrHeader); //备注:当send方法成功后才可调用该方法。如果服务器返回的文档类型为"text/xml",则这句话 //xmlhttp.getResponseHeader("Content-Type");将返回字符串"text/xml"。可以使用getAllResponseHeaders方法获取完整的http头信息。 alert(xmlhttp.getResponseHeader("Content-Type"));//输出http头中的Content-Type列:当前web服务器的版本及名称。 document.frmTest.myButton.disabled=true; //方法:abort //取消当前请求 //语法:oXMLHttpRequest.abort(); //备注:调用此方法后,当前请求返回UNINITIALIZED状态。 //xmlhttp.abort(); //方法:setRequestHeader //单独指定请求的某个http头 //语法:oXMLHttpRequest.setRequestHeader(bstrHeader,bstrValue); //参数:bstrHeader(字符串,头名称。) //bstrValue(字符串,值。) //备注:如果已经存在已此名称命名的http头,则覆盖之。此方法必须在open方法后调用。 //xmlhttp.setRequestHeader(bstrHeader,bstrValue); } functionHandleStateChange() { //属性:readyState //返回XMLHTTP请求的当前状态 //语法:lValue=oXMLHttpRequest.readyState; //备注:变量,此属性只读,状态用长度为4的整型表示.定义如下: //0(未初始化)对象已建立,但是尚未初始化(尚未调用open方法) //1(初始化)对象已建立,尚未调用send方法 //2(发送数据)send方法已调用,但是当前的状态及http头未知 //3(数据传送中)已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误, //4(完成)数据接收完毕,此时可以通过通过responseBody和responseText获取完整的回应数据 if(xmlhttp.readyState==4){ document.frmTest.myButton.disabled=false; //属性:responseBody //返回某一格式的服务器响应数据 //语法:strValue=oXMLHttpRequest.responseBody; //备注:变量,此属性只读,以unsignedarray格式表示直接从服务器返回的未经解码的二进制数据。 alert(xmlhttp.responseBody); //属性:responseStream //以AdoStream对象的形式返回响应信息 //语法:strValue=oXMLHttpRequest.responseStream; //备注:变量,此属性只读,以AdoStream对象的形式返回响应信息。 alert(xmlhttp.responseStream); //属性:responseText //将响应信息作为字符串返回 //语法:strValue=oXMLHttpRequest.responseText; //备注:变量,此属性只读,将响应信息作为字符串返回。XMLHTTP尝试将响应信息解码为Unicode字符串, //XMLHTTP默认将响应数据的编码定为UTF-8,如果服务器返回的数据带BOM(byte-ordermark),XMLHTTP可 //以解码任何UCS-2(bigorlittleendian)或者UCS-4数据。注意,如果服务器返回的是xml文档,此属 //性并不处理xml文档中的编码声明。你需要使用responseXML来处理。 alert(xmlhttp.responseText); //属性:responseXML //将响应信息格式化为XmlDocument对象并返回 //语法:varobjDispatch=oXMLHttpRequest.responseXML; //备注:变量,此属性只读,将响应信息格式化为XmlDocument对象并返回。如果响应数据不是有效的XML文档, //此属性本身不返回XMLDOMParseError,可以通过处理过的DOMDocument对象获取错误信息。 alert("Result="+xmlhttp.responseXML.xml); //属性:status //返回当前请求的http状态码 //语法:lValue=oXMLHttpRequest.status; //返回值:长整形标准http状态码,定义如下: //Number:Description //100:Continue //101:Switchingprotocols //200:OK //201:Created //202:Accepted //203:Non-AuthoritativeInformation //204:NoContent //205:ResetContent //206:PartialContent //300:MultipleChoices //301:MovedPermanently //302:Found //303:SeeOther //304:NotModified //305:UseProxy //307:TemporaryRedirect //400:BadRequest //401:Unauthorized //402:PaymentRequired //403:Forbidden //404:NotFound //405:MethodNotAllowed //406:NotAcceptable //407:ProxyAuthenticationRequired //408:RequestTimeout //409:Conflict //410:Gone //411:LengthRequired //412:PreconditionFailed //413:RequestEntityTooLarge //414:Request-URITooLong //415:UnsupportedMediaType //416:RequestedRangeNotSuitable //417:ExpectationFailed //500:InternalServerError //501:NotImplemented //502:BadGateway //503:ServiceUnavailable //504:GatewayTimeout //505:HTTPVersionNotSupported //备注:长整形,此属性只读,返回当前请求的http状态码,此属性仅当数据发送并接收完毕后才可获取。 alert(xmlhttp.status); //属性:statusText //返回当前请求的响应行状态 //语法:strValue=oXMLHttpRequest.statusText; //备注:字符串,此属性只读,以BSTR返回当前请求的响应行状态,此属性仅当数据发送并接收完毕后才可获取。 alert(xmlhttp.statusText); } } //--> </script> </head> <body> <formname="frmTest"> <inputname="myButton"type="button"value="ClickMe"onclick="PostOrder('http://localhost/example.htm');"> </form> </body> </html>
简单一点来说,就是利用XMLHttpRequest对象向服务器发出了请求,然后获取服务器返回信息这样一个过程,
以上是JavaScript的Ajax技术原理。和后面要说的Jsonp实现跨域访问的原理完全不一样。
###JQuery的AJax
JQuery对ajax这种技术进行了封装,使用起来更为方便。
$.ajax的一般形式
$.ajax({ type:'POST', url:url, data:data, dataType:dataType success:success, });
在场景不一样的时候,我们需要变换使用Ajax。1.拼装json数据。2.序列化表格内容。varformParam=$("#form1").serialize();3.拼接URL。。。比如我们的数据中有特殊字符串(比如&)的时候拼接字符串不好用,可能会使提交内容不完整。这时候采用Json的形式会比较好用。
###利用jsonp实现跨域访问
什么是Jsonp?
和json有什么关系?
jsonp是怎么实现跨域访问的?
首先解释一下,为什么Ajax不能跨域访问,浏览器为什么限制跨域访问。
假设浏览器支持跨域访问,我们可以在A站通过XmlHttpRequest访问B站,这时候已经通过了B站的验证,得到了B站的Cookie,然后我们就可以随意访问B站了,这时候A站冒用B站的身份可以操作B站一切不需要进一步验证的操作,这是相当危险的。
我们如何获取跨域的数据呢?
我们发现Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如script、img、iframe等等。我们可以利用js的这一性质来获取我们想要的数据。
为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
下面我们来看一下jsonp到底做了什么事情。
1、我们知道,哪怕跨域js文件中的代码(当然指符合web脚本安全策略的),web页面也是可以无条件执行的。远程服务器remoteserver.com根目录下有个remote.js文件代码如下:
alert('我是远程文件');
本地服务器localserver.com下有个jsonp.html页面代码如下:
<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <htmlxmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <scripttype="text/javascript"src="http://remoteserver.com/remote.js"></script> </head> <body> </body> </html>
毫无疑问,页面将会弹出一个提示窗体,显示跨域调用成功。这就是最基本的jsonp的思想。
2、现在我们在jsonp.html页面定义一个函数,然后在远程remote.js中传入数据进行调用。jsonp.html页面代码如下:
<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <htmlxmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <scripttype="text/javascript"> varlocalHandler=function(data){ alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:'+data.result); }; </script> <scripttype="text/javascript"src="http://remoteserver.com/remote.js"></script> </head> <body> </body> </html>
remote.js文件代码如下:
localHandler({"result":"我是远程js带来的数据"});
运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程js调用成功,并且还接收到了远程js带来的数据。跨域访问数据的目的已经实现了,可是我怎么让远程js知道它应该调用的本地函数叫什么名字呢?
3.可以传一个参数过去告诉服务端“我想要一段调用XXX函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。
<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <htmlxmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <scripttype="text/javascript"> //得到航班信息查询结果后的回调函数 varflightHandler=function(data){ alert('你查询的航班结果是:票价'+data.price+'元,'+'余票'+data.tickets+'张。'); }; //提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码) varurl="http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler"; //创建script标签,设置其属性 varscript=document.createElement('script'); script.setAttribute('src',url); //把script标签加入head,此时调用开始 document.getElementsByTagName('head')[0].appendChild(script); </script> </head> <body> </body> </html>
在服务器端,我们获取callback,并且拼装好需要的js。
Stringcallback=request.getParemeter("callback");
response.getWriter.print(callback+"("+json+")");
这样返回给页面的内容是:
flightHandler({ "code":"CA1998", "price":1780, "tickets":5 });
4.Jquery也对jsonp实现了封装。(形式比较像ajax)
<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <htmlxmlns="http://www.w3.org/1999/xhtml"> <head> <title>UntitledPage</title> <scripttype="text/javascript"src=jquery.min.js"></script> <scripttype="text/javascript"> jQuery(document).ready(function(){ $.ajax({ type:"get", async:false, url:"http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998", dataType:"jsonp", jsonp:"callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback) jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据 success:function(json){ alert('您查询到航班信息:票价:'+json.price+'元,余票:'+json.tickets+'张。'); }, error:function(){ alert('fail'); } }); }); </script> </head> <body> </body> </html>
最后申明,Ajax和jsonp是两个完全不一样的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加script标签来调用服务器提供的js脚本。
在jQuery中如何通过JSONP来跨域获取数据
第一种方法是在ajax函数中设置dataType为'jsonp':
$.ajax({ dataType:'jsonp', jsonp:'callback', url:'http://www.a.com/user?id=123', success:function(data){ //处理data数据 } });
第二种方法是利用getJSON来实现,只要在地址中加上callback=?参数即可:
$.getJSON('http://www.a.com/user?id=123&callback=?',function(data){ //处理data数据 });
也可以简单地使用getScript方法:
//此时也可以在函数外定义foo方法 functionfoo(data){ //处理data数据 } $.getJSON('http://www.a.com/user?id=123&callback=foo');
JSONP的应用
JSONP在开放API中可以起到非常重要的作用,开放API是运用在开发者自己的应用上,而许多应用往往是在开发者的服务器上而不是在新浪微博的服务器上,因此跨域请求数据成为开发者们所需要解决的一大问题,广大开放平台应该实现对JSONP的支持,这一点新浪微博开放平台便做的非常好(虽然某些API里没有说明,但实际上是可以使用JSONP方式调用的)。