1、Django先後端交互之Ajax和跨域問題

1、Ajax介紹

一、概述javascript

AJAX 是一種在無需從新加載整個網頁的狀況下,可以更新部分網頁的技術。AJAXAsynchronous Javascript And XML)翻譯成中文就是異步JavascriptXML」。即:使用Javascript語言與服務器進行異步交互,傳輸的數據爲XML(固然,傳輸的數據不僅是XML,好比還有JSON)。html

  • 同步交互:客戶端發出一個請求後,須要等待服務器響應結束後,才能發出第二個請求;
  • 異步交互:客戶端發出一個請求後,無需等待服務器響應結束,就能夠發出第二個請求。

二、使用場景前端

  • 搜索引擎(谷歌、百度)在用戶輸入某個關鍵字後,會出來一串提示關鍵字;
  • 註冊頁面,輸入信息後,若是信息有重複或缺失,會自動提示;
  • 當咱們只要修改網頁上的部份內容時,單獨局部刷新就能夠作到;

三、優缺點java

優勢:

  • AJAX使用Javascript技術向服務器發送異步請求;
  • AJAX無須刷新整個頁面;
  • 由於服務器響應內容再也不是整個頁面,而是頁面中的局部,因此AJAX性能高;

缺點:

  • AJAX並不適合全部場景,不少時候仍是要使用同步交互;
  • AJAX雖然提升了用戶體驗,但無形中向服務器發送的請求次數增多了,致使服務器壓力增大;
  • 由於AJAX是在瀏覽器中使用Javascript技術完成的,因此還須要處理瀏覽器兼容性問題,固然,若是使用jQuery的話,則不用考慮這個問題;

2、經過JavaScript實現Ajax

一、過程和請求相關jquery

使用Ajax的過程:

  • 建立核心對象,根據不一樣瀏覽器版本新建核心對象(主要緣由是瀏覽器兼容問題);
  • 使用核心對象打開與服務器的鏈接(open方法);
  • 發送請求(send方法)
  • 註冊監聽,監聽服務器響應。(經過判斷核心對象的就緒程度和狀態碼)

 

XMLHTTPRequest關鍵知識點:

  • open(請求方式, URL, 是否異步)
  • send(請求體)
  • onreadystatechange,指定監聽函數,它會在xmlHttp對象的狀態發生變化時被調用
  • readyState,當前xmlHttp對象的狀態,其中4狀態表示服務器響應結束
  • status:服務器響應的狀態碼,只有服務器響應結束時纔有這個東東,200表示響應成功;
  • responseText:獲取服務器的響應體,文本
  • responseXML:獲取服務器的響應體,XML

二、經過JavaScript實現Ajax之無後臺交互web

<html>
<head>
<script type="text/javascript">
function loadXMLDoc()
{
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");
  }
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" onclick="loadXMLDoc()">經過 AJAX 改變內容</button>

</body>
</html>

  

三、經過JavaScript實現Ajax之與後臺交互ajax

經過Django實現,其實惟一區別就是在open的時候,把URL修改爲Django的一個URL便可,包括GET和POST方法的URL。json

a、urls.py文件後端

urlpatterns = [
    url(r'^ajax_get',views.ajax_get),
    url(r'^ajax_post',views.ajax_post)
]

  

b、views.py文件api

def ajax_get(req):
    return render(req,'ajax_get.html')

def ajax_post(req):
    if req.method=='POST':
        print req.POST
    else:
        print req.GET
    return 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;  //定義局部變量xmlhttp
                if (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" onclick="loadXMLDoc()">修改</button>
</body>
</html>

  

  

 d、測試結果

GET方法:

 

 

 

POST方法:

 

3、經過jQuery實現Ajax

 一、兩個快捷API

<1>$.get(url, [data], [callback], [type])
<2>$.post(url, [data], [callback], [type])  
url:請求的URL
data:發送的數據
callback:回調函數,前端發送數據給後端以後,後端返回數據給前端,前端接受到的數據
type:text|html|json|script

  

 二、兩個不經常使用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);
  });
});

  

三、最經常使用的方法

$.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)。一般在本地和遠程的內容編碼不一樣時使用。

  

  

四、Django中設定狀態碼

HttpResponse.status_code = 400 //手動設定返回的狀態碼爲400


4、跨域問題

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的講解有不少,大概總結以下:

一、一個衆所周知的問題,Ajax直接請求普通文件存在跨域無權限訪問的問題,甭管你是靜態頁面、動態網頁、web服務、WCF,只要是跨域請求,一概不許;

二、不過咱們又發現,Web頁面上調用js文件時則不受是否跨域的影響(不只如此,咱們還發現凡是擁有"src"這個屬性的標籤都擁有跨域的能力,好比<script>、<img>、<iframe>);

三、因而能夠判斷,當前階段若是想經過純web端(ActiveX控件、服務端代理、屬於將來的HTML5之Websocket等方式不算)跨域訪問數據就只有一種可能,那就是在遠程服務器上設法把數據裝進js格式的文件裏,供客戶端調用和進一步處理;

四、恰巧咱們已經知道有一種叫作JSON的純字符數據格式能夠簡潔的描述複雜數據,更妙的是JSON還被js原生支持,因此在客戶端幾乎能夠爲所欲爲的處理這種格式的數據;

五、這樣子解決方案就呼之欲出了,web客戶端經過與調用腳本如出一轍的方式,來調用跨域服務器上動態生成的js格式文件(通常以JSON爲後綴),顯而易見,服務器之因此要動態生成JSON文件,目的就在於把客戶端須要的數據裝入進去。

六、客戶端在對JSON文件調用成功以後,也就得到了本身所需的數據,剩下的就是按照本身需求進行處理和展示了,這種獲取遠程數據的方式看起來很是像AJAX,但其實並不同。

七、爲了便於客戶端使用數據,逐漸造成了一種非正式傳輸協議,人們把它稱做JSONP,該協議的一個要點就是容許用戶傳遞一個callback參數給服務端,而後服務端返回數據時會將這個callback參數做爲函數名來包裹住JSON數據,這樣客戶端就能夠隨意定製本身的函數來自動處理返回數據了。

若是對於callback參數如何使用還有些模糊的話,咱們後面會有具體的實例來說解。

 

4.4 JavaScript實現跨域

一、咱們知道,哪怕跨域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>
複製代碼

  

毫無疑問,頁面將會彈出一個提示窗體,顯示跨域調用成功。

 

二、如今咱們在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的服務者都要面對不少服務對象,而這些服務對象各自的本地函數都不相同啊?咱們接着往下看。

三、聰明的開發者很容易想到,只要服務端提供的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的總結

一、ajax和jsonp這兩種技術在調用方式上「看起來」很像,目的也同樣,都是請求一個url,而後把服務器返回的數據進行處理,所以jquery和ext等框架都把jsonp做爲ajax的一種形式進行了封裝;

二、但ajax和jsonp其實本質上是不一樣的東西。ajax的核心是經過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態添加<script>標籤來調用服務器提供的js腳本。

三、因此說,其實ajax與jsonp的區別不在因而否跨域,ajax經過服務端代理同樣能夠實現跨域,jsonp自己也不排斥同域的數據的獲取。

四、還有就是,jsonp是一種方式或者說非強制性協議,如同ajax同樣,它也不必定非要用json格式來傳遞數據,若是你願意,字符串都行,只不過這樣不利於用jsonp提供公開服務。

總而言之,jsonp不是ajax的一個特例,哪怕jquery等巨頭把jsonp封裝進了ajax,也不能改變這一點!

相關文章
相關標籤/搜索