1、傳統(tǒng)的Web應(yīng)用
一個簡單操作需要重新加載全局數(shù)據(jù)
2、AJAX
AJAX,Asynchronous JavaScript and XML (異步的JavaScript和XML),一種創(chuàng)建交互式網(wǎng)頁應(yīng)用的網(wǎng)頁開發(fā)技術(shù)方案。
- 異步的JavaScript:
使用 【JavaScript語言】 以及 相關(guān)【瀏覽器提供類庫】 的功能向服務(wù)端發(fā)送請求,當服務(wù)端處理完請求之后,【自動執(zhí)行某個JavaScript的回調(diào)函數(shù)】。
PS:以上請求和響應(yīng)的整個過程是【偷偷】進行的,頁面上無任何感知。- XML
XML是一種標記語言,是Ajax在和后臺交互時傳輸數(shù)據(jù)的格式之一利用AJAX可以做:
1、注冊時,輸入用戶名自動檢測用戶是否已經(jīng)存在。
2、登陸時,提示用戶名密碼錯誤
3、刪除數(shù)據(jù)行時,將行ID發(fā)送到后臺,后臺在數(shù)據(jù)庫中刪除,數(shù)據(jù)庫刪除成功后,在頁面DOM中將數(shù)據(jù)行也刪除。(博客園)
由于HTML標簽的iframe標簽具有局部加載內(nèi)容的特性,所以可以使用其來偽造Ajax請求。
iframe.src
重新加載,頁面不刷新
<!DOCTYPE html><html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <div> <p>請輸入要加載的地址:<span id="currentTime"></span></p> <p> <input id="url" type="text" /> <input type="button" value="刷新" onclick="LoadPage();"> </p> </div> <div> <h3>加載頁面位置:</h3> <iframe id="iframePosition" style="width: 100%;height: 500px;"></iframe> </div> <script type="text/javascript"> window.onload= function(){ var myDate = new Date(); document.getElementById('currentTime').innerText = myDate.getTime(); }; function LoadPage(){ var targetUrl = document.getElementById('url').value; document.getElementById("iframePosition").src = targetUrl; } </script> </body></html>
Ajax主要就是使用 【XmlHttpRequest】對象來完成請求的操作,該對象在主流瀏覽器中均存在(除早起的IE),Ajax首次出現(xiàn)IE5.5中存在(ActiveX控件)。
1、XmlHttpRequest對象介紹
XmlHttpRequest對象的主要方法:
XmlHttpRequest對象的主要屬性:
2、跨瀏覽器支持
XmlHttpRequest
IE7+, Firefox, Chrome, Opera, etc.
ActiveXObject("Microsoft.XMLHTTP")
IE6, IE5
原生ajax發(fā)送post請求要帶上請求頭
1 | xhr.setRequestHeader( 'Content-Type' , 'application/x-www-form-urlencoded; charset-UTF-8' ); |
jQuery其實就是一個JavaScript的類庫,其將復(fù)雜的功能做了上層封裝,使得開發(fā)者可以在其基礎(chǔ)上寫更少的代碼實現(xiàn)更多的功能。
注:2.+版本不再支持IE9以下的瀏覽器
通過ajax返回得到的字符串,可以通過 obj = JSON.parse(callback)
轉(zhuǎn)換回原來格式!
由于瀏覽器存在同源策略機制,同源策略阻止從一個源加載的文檔或腳本獲取或設(shè)置另一個源加載的文檔的屬性。
特別的:由于同源策略是瀏覽器的限制,所以請求的發(fā)送和響應(yīng)是可以進行,只不過瀏覽器不接受罷了。
瀏覽器同源策略并不是對所有的請求均制約:
制約: XmlHttpRequest
不叼: img、iframe、script等具有src屬性的標簽 => 相當于發(fā)送了get請求
跨域,跨域名訪問,如:http://www.c1.com 域名向 http://www.c2.com域名發(fā)送請求。
1、JSONP實現(xiàn)跨域請求
JSONP(JSONP - JSON with Padding是JSON的一種“使用模式”),利用script標簽的src屬性(瀏覽器允許script標簽跨域)
-- localhost:8889 :class MainHandler(tornado.web.RequestHandler): def get(self): self.write("func([11,22,33,44])")*******************************************-- localhost:8888 :function Jsonp1(){ var tag = document.createElement('script'); tag.src = 'http://localhost:8889/index'; document.head.appendChild(tag); document.head.removeChild(tag);}function func(arg) { console.log(arg)}
Jsonp1()
被執(zhí)行后,創(chuàng)建了一個<script>
代碼塊 , 填充了返回值!
1 2 3 | <script> func([ 11 , 22 , 33 , 44 ]) < / script> |
接著執(zhí)行了 func()
輸出 [11,22,33,44]
-- jQuery 實現(xiàn)方式:
function jsonpclick() { $.ajax({ url:'http://localhost:8889/index', dataType:'jsonp', jsonpCallback:'func', });}
改進版:
-- localhost:8889 :class MainHandler(tornado.web.RequestHandler): def get(self): callback = self.get_argument('callback') self.write("%s([11,22,33,44])"%callback)此時頁面中,訪問:http://localhost:8889/index?callback=xxoohttp://localhost:8889/index?callback=ssss都可以!*******************************************-- localhost:8888 :function func(arg) { console.log(arg)}function jsonpclick() { $.ajax({ url:'http://localhost:8889/index', dataType:'jsonp', jsonp:'callback', jsonpCallback:'func', });}
代碼中相當于發(fā)送了: http://localhost:8889/index?callback=func
1 2 3 4 5 6 | jsonp:要求為String類型的參數(shù),在一個jsonp請求中重寫回調(diào)函數(shù)的名字。 該值用來替代在 "callback=?" 這種GET或POST請求中URL參數(shù)里的 "callback" 部分, 例如 {jsonp: 'onJsonPLoad' } 會導(dǎo)致將 "?onJsonPLoad=" 傳給服務(wù)器。 jsonpCallBack: 區(qū)分大小寫 |
2、CORS
隨著技術(shù)的發(fā)展,現(xiàn)在的瀏覽器可以支持主動設(shè)置從而允許跨域請求,
即:跨域資源共享(CORS,Cross-Origin Resource Sharing)
其本質(zhì)是設(shè)置響應(yīng)頭,使得瀏覽器允許跨域請求。
* 簡單請求 OR 非簡單請求
1 2 3 4 5 6 7 8 9 10 11 12 13 | 條件: 1 、請求方式:HEAD、GET、POST 2 、請求頭信息: Accept Accept - Language Content - Language Last - Event - ID Content - Type 對應(yīng)的值是以下三個中的任意一個 application / x - www - form - urlencoded multipart / form - data text / plain 注意:同時滿足以上兩個條件時,則是簡單請求,否則為復(fù)雜請求 |
* 簡單請求和非簡單請求的區(qū)別?
1 2 | 簡單請求:一次請求 非簡單請求:兩次請求,在發(fā)送數(shù)據(jù)之前會先發(fā)一次請求用于做“預(yù)檢”,只有“預(yù)檢”通過后才再發(fā)送一次請求用于數(shù)據(jù)傳輸。 |
* 關(guān)于“預(yù)檢”
1 2 3 4 5 6 7 | - 請求方式:OPTIONS - “預(yù)檢”其實做檢查,檢查如果通過則允許傳輸數(shù)據(jù),檢查不通過則不再發(fā)送真正想要發(fā)送的消息 - 如何“預(yù)檢” = > 如果復(fù)雜請求是PUT等請求,則服務(wù)端需要設(shè)置允許某請求,否則“預(yù)檢”不通過 Access - Control - Request - Method = > 如果復(fù)雜請求設(shè)置了請求頭,則服務(wù)端需要設(shè)置允許某請求頭,否則“預(yù)檢”不通過 Access - Control - Request - Headers |
基于cors實現(xiàn)AJAX請求:
a、支持跨域,簡單請求
1 2 3 4 5 | 服務(wù)器設(shè)置響應(yīng)頭:Access - Control - Allow - Origin = '域名' 或 '*' self .set_header( 'Access-Control-Allow-Origin' , "http://localhost:8888" ) self .set_header( 'Access-Control-Allow-Origin' , "http://localhost:8888,http://localhost:9999" ) self .set_header( 'Access-Control-Allow-Origin' , "*" ) / / 所有網(wǎng)站 |
實例:
function corsSimple() { $.ajax({ url:'http://localhost:8889/index', type:'post', data:{'v1':'k1'}, success:function (callback) { console.log(callback) } });}***********************************************-- localhost:8889def post(self, *args, **kwargs): self.set_header('Access-Control-Allow-Origin', "http://localhost:8888") v1 = self.get_argument('v1') print(v1) self.write('--post--')
b、支持跨域,復(fù)雜請求
由于復(fù)雜請求時,首先會發(fā)送“預(yù)檢”請求,如果“預(yù)檢”成功,則發(fā)送真實數(shù)據(jù)。
“預(yù)檢”請求時,允許請求方式則需服務(wù)器設(shè)置響應(yīng)頭:Access-Control-Request-Method
“預(yù)檢”請求時,允許請求頭則需服務(wù)器設(shè)置響應(yīng)頭:Access-Control-Request-Headers
“預(yù)檢”緩存時間,服務(wù)器設(shè)置響應(yīng)頭:Access-Control-Max-Age
--localhost:8889def options(self, *args, **kwargs): self.set_header('Access-Control-Allow-Methods', "PUT") self.set_header('Access-Control-Allow-Origin', "http://localhost:8888") print('--option--')def put(self, *args, **kwargs): self.set_header('Access-Control-Allow-Origin', "http://localhost:8888") print('--put--') self.write('--put--')***********************************************--localhost:8888function corscomplex() { $.ajax({ url:'http://localhost:8889/index', type:'put', data:{'v1':'k1'}, success:function (callback) { console.log(callback) } });}
如果客戶端,加上了自定義請求頭,服務(wù)器端要加上
1 | self .set_header( 'Access-Control-Allow-Headers' , "key1,key2" ) |
實例:
--localhost:8889def options(self, *args, **kwargs): self.set_header('Access-Control-Allow-Methods', "PUT") self.set_header('Access-Control-Allow-Origin', "http://localhost:8888") self.set_header('Access-Control-Allow-Headers', "key1,key2") self.set_header('Access-Control-Max-Age', 10) print('--option--')def put(self, *args, **kwargs): self.set_header('Access-Control-Allow-Origin', "http://localhost:8888") print('--put--') self.write('--put--')***********************************************--localhost:8888function corscomplex() { $.ajax({ url:'http://localhost:8889/index', type:'put', headers:{'key1':'xxx'}, data:{'v1':'k1'}, success:function (callback) { console.log(callback) } });}
控制預(yù)檢過期時間:
1 | self .set_header( 'Access-Control-Max-Age' , 10 ) / / 10 秒 |
c、跨域傳輸cookie
在跨域請求中,默認情況下,HTTP Authentication信息,Cookie頭以及用戶的SSL證書無論在預(yù)檢請求中或是在實際請求都是不會被發(fā)送。
如果想要發(fā)送:
瀏覽器端:XMLHttpRequest的withCredentials為true
服務(wù)器端:Access-Control-Allow-Credentials為true
注意:服務(wù)器端響應(yīng)的 Access-Control-Allow-Origin 不能是通配符 *
--localhost:8889def options(self, *args, **kwargs): self.set_header('Access-Control-Allow-Methods', "PUT") self.set_header('Access-Control-Allow-Origin', "http://localhost:8888") self.set_header('Access-Control-Allow-Credentials', "true") //必須 print('--option--')def put(self, *args, **kwargs): self.set_header('Access-Control-Allow-Origin', "http://localhost:8888") self.set_header('Access-Control-Allow-Credentials', "true") //必須print(self.cookies) self.set_cookie('k1','kkk') self.write('--put--')***********************************************--localhost:8888function corscomplex() { $.ajax({ url:'http://localhost:8889/index', type:'put', data:{'v1':'k1'}, xhrFields:{withCredentials: true}, success:function (callback) { console.log(callback) } });}
d、跨域獲取響應(yīng)頭
默認獲取到的所有響應(yīng)頭只有基本信息,如果想要獲取自定義的響應(yīng)頭,則需要再服務(wù)器端設(shè)置Access-Control-Expose-Headers。
--localhost:8889def options(self, *args, **kwargs): self.set_header('Access-Control-Allow-Methods', "PUT") self.set_header('Access-Control-Allow-Origin', "http://localhost:8888") self.set_header('Access-Control-Allow-Headers', "key1,key2") self.set_header('Access-Control-Allow-Credentials', "true") print('--option--')def put(self, *args, **kwargs): self.set_header('Access-Control-Allow-Origin', "http://localhost:8888") self.set_header('Access-Control-Allow-Credentials', "true") self.set_header('bili', "daobidao") //設(shè)置響應(yīng)頭 self.set_header('Access-Control-Expose-Headers', "xxoo,bili") //允許發(fā)送 print(self.cookies) self.set_cookie('k1','kkk') self.write('--put--')***********************************************--localhost:8888function corsRequest() { $.ajax({ url:'http://localhost:8889/index', type:'put', data:{'v1':'k1'}, xhrFields:{withCredentials: true}, success:function (callback,statusText, xmlHttpRequest) { console.log(callback); console.log(xmlHttpRequest.getAllResponseHeaders()); } });}
示例代碼整合: