一、Django前后端交互之Ajax和跨域问题
一、Ajax介绍
1、概述
AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即:使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,比如还有JSON)。
- 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
- 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
2、使用场景
- 搜索引擎(谷歌、百度)在用户输入某个关键字后,会出来一串提示关键字;
- 注册页面,输入信息后,如果信息有重复或缺失,会自动提示;
- 当我们只要修改网页上的部分内容时,单独局部刷新就可以做到;
3、优缺点
优点:
- AJAX使用Javascript技术向服务器发送异步请求;
- AJAX无须刷新整个页面;
- 因为服务器响应内容不再是整个页面,而是页面中的局部,所以AJAX性能高;
缺点:
- AJAX并不适合所有场景,很多时候还是要使用同步交互;
- AJAX虽然提高了用户体验,但无形中向服务器发送的请求次数增多了,导致服务器压力增大;
- 因为AJAX是在浏览器中使用Javascript技术完成的,所以还需要处理浏览器兼容性问题,当然,如果使用jQuery的话,则不用考虑这个问题;
二、通过JavaScript实现Ajax
1、过程和请求相关
使用Ajax的过程:
- 创建核心对象,根据不同浏览器版本新建核心对象(主要原因是浏览器兼容问题);
- 使用核心对象打开与服务器的连接(open方法);
- 发送请求(send方法)
- 注册监听,监听服务器响应。(通过判断核心对象的就绪程度和状态码)
XMLHTTPRequest关键知识点:
- open(请求方式, URL, 是否异步)
- send(请求体)
- onreadystatechange,指定监听函数,它会在xmlHttp对象的状态发生变化时被调用
- readyState,当前xmlHttp对象的状态,其中4状态表示服务器响应结束
- status:服务器响应的状态码,只有服务器响应结束时才有这个东东,200表示响应成功;
- responseText:获取服务器的响应体,文本
- responseXML:获取服务器的响应体,XML
2、通过JavaScript实现Ajax之无后台交互
<html> <head> <script type="text/javascript"> function loadXMLDoc() { var xmlhttp; if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safarixmlhttp=new XMLHttpRequest();} else{// code for IE6, IE5xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");} xmlhttp.onreadystatechange=function(){if (xmlhttp.readyState==4 && xmlhttp.status==200){document.getElementById("myDiv").innerHTML=xmlhttp.responseText;}} xmlhttp.open("GET","/ajax/test1.txt",true); xmlhttp.send(); } </script> </head> <body><div id="myDiv"><h2>Let AJAX change this text</h2></div> <button type="button" οnclick="loadXMLDoc()">通过 AJAX 改变内容</button></body> </html>
3、通过JavaScript实现Ajax之与后台交互
通过Django实现,其实唯一区别就是在open的时候,把URL修改成Django的一个URL即可,包括GET和POST方法的URL。
a、urls.py文件
urlpatterns = [url(r'^ajax_get',views.ajax_get),url(r'^ajax_post',views.ajax_post) ]
b、views.py文件
def ajax_get(req):return render(req,'ajax_get.html')def ajax_post(req):if req.method=='POST':print req.POSTelse:print req.GETreturn HttpResponse('ajax_post')
c、ajax_get.html文件
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>测试ajax的GET方法</title><script>function loadXMLDoc(){var xmlhttp; //定义局部变量xmlhttpif (window.XMLHttpRequest) //XMLHttpRequet对象用于和服务器交互数据。{// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码xmlhttp=new XMLHttpRequest();}else{// IE6, IE5 浏览器执行代码xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}//侦听xmlhttp.onreadystatechange=function(){if (xmlhttp.readyState==4 && xmlhttp.status==200){document.getElementById("myDiv").innerHTML=xmlhttp.responseText;}}//POST方法xmlhttp.open("POST","./ajax_post",true);//GET方法//xmlhttp.open("GET","./user_info",true);//设置头部信息xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");//POST method发送xmlhttp.send("name=xuequn");//GET method发送//xmlhttp.send();} </script> </head> <body><div id="myDiv"><h2>使用 AJAX 修改该文本内容</h2></div><button type="button" οnclick="loadXMLDoc()">修改</button> </body> </html>
d、测试结果
GET方法:
POST方法:
三、通过jQuery实现Ajax
1、两个快捷API
<1>$.get(url, [data], [callback], [type]) <2>$.post(url, [data], [callback], [type]) url:请求的URL data:发送的数据 callback:回调函数,前端发送数据给后端之后,后端返回数据给前端,前端接受到的数据 type:text|html|json|script
2、两个不常用API
getJSON(url,data,callback)
<html> <head> <script type="text/javascript" src="/jquery/jquery.js"></script> <script type="text/javascript"> $(document).ready(function(){$("button").click(function(){$.getJSON("/example/jquery/demo_ajax_json.js",function(result){$.each(result, function(i, field){$("p").append(field + " ");});});}); }); </script> </head><body><button>获得 JSON 数据</button> <p></p></body> </html>///example/jquery/demo_ajax_json.js { "firstName": "Bill","lastName": "Gates","age": 60 }
getScript( url ,callback ])
jQuery.getScript("http://dev.jquery.com/view/trunk/plugins/color/jquery.color.js",function(){$("#go").click(function(){$(".block").animate( { backgroundColor: 'pink' }, 1000).animate( { backgroundColor: 'blue' }, 1000);}); });
3、最常用的方法
$.ajax()
$(function(){$('#send').click(function(){$.ajax({type: "GET",url: "test.json",data: {username:$("#username").val(), content:$("#content").val()},dataType: "json",success: function(data){$('#resText').empty(); //清空resText里面的所有内容var html = ''; $.each(data, function(commentIndex, comment){html += '<div class="comment"><h6>' + comment['username']+ ':</h6><p class="para"' + comment['content']+ '</p></div>';});$('#resText').html(html);}});}); });
ajax的参数
1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址。2.type: 要求为String类型的参数,请求方式(post或get)默认为get。注意其他http请求方法,例如put和delete也可以使用,但仅部分浏览器支持。3.timeout: 要求为Number类型的参数,设置请求超时时间(毫秒)。此设置将覆盖$.ajaxSetup()方法的全局设置。4.async: 要求为Boolean类型的参数,默认设置为true,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为false。注意,同步请求将锁住浏览器,用户其他操作必须等待请求完成才可以执行。5.cache: 要求为Boolean类型的参数,默认为true(当dataType为script时,默认为false),设置为false将不会从浏览器缓存中加载请求信息。6.data: 要求为Object或String类型的参数,发送到服务器的数据。如果已经不是字符串,将自动转换为字符串格式。get请求中将附加在url后。防止这种自动转换,可以查看processData选项。 对象必须为key/value格式,例如{foo1:"bar1",foo2:"bar2"}转换为&foo1=bar1&foo2=bar2。如果是数组,JQuery将自动为不同值对应同一个名称。例如{foo:["bar1","bar2"]}转换为&foo=bar1&foo=bar2。7.dataType: 要求为String类型的参数,预期服务器返回的数据类型。如果不指定,JQuery将自动根据http包mime信息返回responseXML或responseText,并作为回调函数参数传递。可用的类型如下: xml:返回XML文档,可用JQuery处理。 html:返回纯文本HTML信息;包含的script标签会在插入DOM时执行。 script:返回纯文本JavaScript代码。不会自动缓存结果。除非设置了cache参数。注意在远程请求时(不在同一个域下),所有post请求都将转为get请求。 json:返回JSON数据。 jsonp:JSONP格式。使用SONP形式调用函数时,例如myurl?callback=?,JQuery将自动替换后一个“?”为正确的函数名,以执行回调函数。 text:返回纯文本字符串。8.beforeSend: 要求为Function类型的参数,发送请求前可以修改XMLHttpRequest对象的函数,例如添加自定义HTTP头。在beforeSend中如果返回false可以取消本次ajax请求。XMLHttpRequest对象是惟一的参数。function(XMLHttpRequest){this; //调用本次ajax请求时传递的options参数} 9.complete: 要求为Function类型的参数,请求完成后调用的回调函数(请求成功或失败时均调用)。参数:XMLHttpRequest对象和一个描述成功请求类型的字符串。function(XMLHttpRequest, textStatus){this; //调用本次ajax请求时传递的options参数}10.success:要求为Function类型的参数,请求成功后调用的回调函数,有两个参数。(1)由服务器返回,并根据dataType参数进行处理后的数据。(2)描述状态的字符串。function(data, textStatus){//data可能是xmlDoc、jsonObj、html、text等等this; //调用本次ajax请求时传递的options参数}11.error: 要求为Function类型的参数,请求失败时被调用的函数。该函数有3个参数,即XMLHttpRequest对象、错误信息、捕获的错误对象(可选)。ajax事件函数如下:function(XMLHttpRequest, textStatus, errorThrown){//通常情况下textStatus和errorThrown只有其中一个包含信息this; //调用本次ajax请求时传递的options参数}12.contentType: 要求为String类型的参数,当发送信息至服务器时,内容编码类型默认为"application/x-www-form-urlencoded"。该默认值适合大多数应用场合。13.dataFilter: 要求为Function类型的参数,给Ajax返回的原始数据进行预处理的函数。提供data和type两个参数。data是Ajax返回的原始数据, type是调用jQuery.ajax时提供的dataType参数。函数返回的值将由jQuery进一步处理。function(data, type){//返回处理后的数据return data;}14.dataFilter: 要求为Function类型的参数,给Ajax返回的原始数据进行预处理的函数。提供data和type两个参数。data是Ajax返回的原始数据, type是调用jQuery.ajax时提供的dataType参数。函数返回的值将由jQuery进一步处理。function(data, type){//返回处理后的数据return data;}15.global: 要求为Boolean类型的参数,默认为true。表示是否触发全局ajax事件。设置为false将不会触发全局ajax事件,ajaxStart或ajaxStop可用于控制各种ajax事件。16.ifModified: 要求为Boolean类型的参数,默认为false。仅在服务器数据改变时获取新数据。服务器数据改变判断的依据是Last-Modified头信息。默认值是false,即忽略头信息。17.jsonp: 要求为String类型的参数,在一个jsonp请求中重写回调函数的名字。该值用来替代在"callback=?"这种GET或POST请求中URL参数里的"callback"部分, 例如{jsonp:'onJsonPLoad'}会导致将"onJsonPLoad=?"传给服务器。18.username: 要求为String类型的参数,用于响应HTTP访问认证请求的用户名。19.password: 要求为String类型的参数,用于响应HTTP访问认证请求的密码。20.processData: 要求为Boolean类型的参数,默认为true。默认情况下,发送的数据将被转换为对象(从技术角度来讲并非字符串)以配合默认内容类型"application/x-www-form-urlencoded"。 如果要发送DOM树信息或者其他不希望转换的信息,请设置为false。21.scriptCharset: 要求为String类型的参数,只有当请求时dataType为"jsonp"或者"script",并且type是GET时才会用于强制修改字符集(charset)。通常在本地和远程的内容编码不同时使用。
4、Django中设定状态码
HttpResponse.status_code = 400 //手动设定返回的状态码为400四、跨域问题
4.1 同源策略机制
浏览器有一个很重要的概念——同源策略(Same-Origin Policy)。所谓同源是指,域名,协议,端口相同。不同源的客户端脚本(javascript、ActionScript)在没明确授权的情况下,不能读写对方的资源。
简单的来说,浏览器允许包含在页面A的脚本访问第二个页面B的数据资源,这一切是建立在A和B页面是同源的基础上。
如果Web世界没有同源策略,当你登录淘宝账号并打开另一个站点时,这个站点上的JavaScript可以跨域读取你的任意账号的数据,这样整个Web世界就无隐私可言了。
4.2 问题出现
在同源策略下,比如我在URL01:http://localhost/user_manager/test_jsonp/上获取https://api.douban.com/v2/book/search?q=javascript&count=1&callback=handleResponse的数据时,就是属于跨域了,此时会出现问题:
代码如下:重点在open里面的URL,是属于跨域的行为。
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>ajax</title> </head> <body><div id="mydiv"><button id="btn">点击</button></div> </body> <script type="text/javascript">window.onload = function() {var oBtn = document.getElementById('btn');oBtn.onclick = function() {var xhr = new XMLHttpRequest();xhr.onreadystatechange = function() {if (xhr.readyState == 4 && xhr.status == 200) {alert( xhr.responseText );}};xhr.open('get', 'https://api.douban.com/v2/book/search?q=javascript&count=1', true);xhr.send(); };}; </script> </html>上述程序运行时会报错:
4.3 JSONP介绍
JSONP 是 JSON with padding(填充式 JSON 或参数式 JSON)的简写。
JSONP实现跨域请求的原理简单的说,就是动态创建<script>标签,然后利用<script>的src 不受同源策略约束来跨域获取数据。
JSONP 由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的 JSON 数据。
其实网上关于JSONP的讲解有很多,大概总结如下:
1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;
2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有"src"这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);
3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;
4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;
5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。
6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。
7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
如果对于callback参数如何使用还有些模糊的话,我们后面会有具体的实例来讲解。
4.4 JavaScript实现跨域
1、我们知道,哪怕跨域js文件中的代码(当然指符合web脚本安全策略的),web页面也是可以无条件执行的,比如img中的src指定的图片地址可以是网上任意图片。
远程服务器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脚本并响应了。
这里其实就是跨域的api接口。
看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 handleResponse = function(data){alert(data.total);};// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)var url = "https://api.douban.com/v2/book/search?q=javascript&count=1&callback=handleResponse";// 创建script标签,设置其属性var script = document.createElement('script');script.setAttribute('src', url);// 把script标签加入head,此时调用开始document.getElementsByTagName('head')[0].appendChild(script);</script> </head> <body></body> </html>这里的API接口是豆瓣的一个获取图书信息的接口,本质就是在指定位置添加了一个script标签,且是通过src方式,然后通过callback参数获取返回值,返回值可以直接使用,这样就做到了跨域。
4.5 jQuery实现跨域
jQuery实现起来会更加简单,因为它已经帮你包装了一层,比如:指定url、数据类型,最重要的是,jQuery里面,直接可以通过success参数调用获取的结果,当然,也可以使用error设定错误提示。
<!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>Untitled Page</title><script src="http://code.jquery.com/jquery-2.1.4.min.js"></script><script type="text/javascript">jQuery(document).ready(function(){$.ajax({type: "get",async: false,url: "https://api.douban.com/v2/book/search?q=javascript&count=1",dataType: "jsonp",jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据success: function(json){alert(json.total);},error: function(){alert('fail');}});});</script></head><body></body></html>
4.6 jsonp的总结
1、ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;
2、但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。
3、所以说,其实ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取。
4、还有就是,jsonp是一种方式或者说非强制性协议,如同ajax一样,它也不一定非要用json格式来传递数据,如果你愿意,字符串都行,只不过这样不利于用jsonp提供公开服务。
总而言之,jsonp不是ajax的一个特例,哪怕jquery等巨头把jsonp封装进了ajax,也不能改变这一点!
转载于:https://www.cnblogs.com/skyflask/p/9459309.html
总结
以上是生活随笔为你收集整理的一、Django前后端交互之Ajax和跨域问题的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: HDU 5115 Dire Wolf
- 下一篇: 暑假结束时