一、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
- 下一篇: 暑假结束时