AngularJS:與后端服務器通訊


 AngularJS使用XMLHttpRequest(XHR)和JSONP請求來與后端通信,它用通用的$http服務來發布XHR和JSONP請求。

$http請求
$http APIs 實例如下:
首先有一個專用的方法發出XHR GET請求,有幾種同樣的其他類型的XHRrequests方法如下:
  Get:$http.get(url,config)
  POST:$http.post(url,data,config)
  Put:$http.put(url,data,config)
  Delete:$http.delete(url,config)
  Head:$http.head
JSONP請求:$http.jsonp(url, config) 
$http方法接受的參數依賴於使用的HTTP方法。對於可以在body中添加數據的方法(如POST和PUT),參數如下:
  url:將要調用的目標URL
  data:將要發送的數據
  config:js對象,包含其他的影響請求和響應的配置選項。
對於其他方法(GET,DELETE,HEAD,JSONP),在請求body中沒有數據發送,參數變得簡單,可以簡化為兩個參數:url和config。
從$http方法返回的對象允許我們注冊success和error回調函數。
 
http響應處理
請求可能會成功或者失敗,AngularJS提供兩種方法來注冊回調函數處理兩種結果:success和error.。所有的方法都接受包含以下參數的回調函數:
  data:實際返回數據
  status:響應的HTTP狀態
  headers:接觸到HTTP響應頭的函數
  config:當請求觸發時提供的配置對象
success的返回碼是:200-299,其他的返回碼是error。如果不注冊回調函數,響應將會忽略。
 
同源策略
1、JSONP
瀏覽器可以使用script標簽從外部服務器拉取js。JSONP不觸發XHR請求,但是產生一個<script>標簽,源指向外部資源。一旦產生的script標簽出現在DOM中,瀏覽器執行其功能,並調用服務器,服務器在我們的web應用中使用函數調用。JSONP實例如下:
調用$http.jsonp函數,AngularJS將動態創建一個新的<script>DOM元素如下:
只要這個script標簽添加到DOM中,瀏覽器將會請求URL,響應一旦返回,將會有內容如下:
一個JSONP響應是一個正常的JS函數調用。AngularJS產生angular.callbacks._k函數,這個函數一旦調用將會觸發success回調函數。提供給$http.jsonp函數調用的URL必須包含JSON_CALLBACK請求參數。AngularJS將這個字符串轉為動態生成的函數名。
 JSONP的限制:
  1)僅能獲取HTTP請求,錯誤處理將成問題,因為瀏覽器不會在返回給js的script標簽中暴露HTTP響應狀態。實際上記錄http狀態錯誤並觸發出錯回調函數很困難。
  2)JSONP也會暴露我們的web應用產生安全問題。除了XSS攻擊,最大的問題是一個服務器能在JSONP響應中產生任意抽象的js。這個js將會被加載到瀏覽器中並在用戶的session中執行,引起不同危害,從簡單的網頁奔潰到竊取敏感數據,所以我們應該通過jsonp請求謹慎選擇服務並且僅使用可信任的服務器。
2、使用CORS
建立於XMLHttpRequest對象之上以一種已定義並可控的方式實現跨域AJAX請求,CORS的整體思想是:瀏覽器和外部服務器需要協作(通過發送合適的請求和響應頭)來有條件的使用跨域請求。所以,外部服務器需要配置。瀏覽器必須能發送合適的請求頭,並對跨域請求成功的服務器響應進行解釋。
CORS請求分為simple(GET POST HEAD請求) 和 non-simple(使用HTTP動詞或者允許集合之外的請求頭)。
對於non-simple的請求,瀏覽器必須在發送最初請求之前發送探測的OPTION請求,並等待服務器的批准。仔細觀察HTTP就會發現OPTIONS請求。一個刪除用戶的例子如下:
檢查HTTP通信可以看到兩個請求(OPTIONS和DELETE)指向同一個URL
3、服務器端代理
JSONP不是一個理想的跨域請求技術。CORS規范使情況好轉,但仍需要在服務器端進行額外的配置,並且瀏覽器需要支持標准。如果無法使用CORS或JSONP技術,就是永遠的選擇完全避免跨域請求的問題。我們可以通過配置本地服務器作為對外部服務器的代理實現這一目標。通過采用正確的服務器配置我們可以通過我們的服務器代理跨域請求,從而讓瀏覽器的目標只有我們的服務器。這種技術適用於所有的瀏覽器,並且不需要提前發生OPTIONS請求。此外,它不會添加任何附加的安全風險。這種方法的缺點是,我們需要相應地配置服務器。 
 
promises 和 $q
$q基礎
舉個例子:我們通過電話預訂pizza並讓其快遞到我們的家。雖然預定pizza只需要一個很短的電話,實際交貨(訂單執行)需要一定的時間,是異步的。為了感受Promise API,我們使用$q來對pizza訂單及其成功交付進行建模。首先,定義一個Person,可以吃掉pizza,或者當訂單未送達時感到飢餓,如下所示:
可以有個eat和beHungry方法當作success和error的回調函數。
現在對pizza預定建模並完成整個過程如下:
$q.defer()返回一個deferred對象,這個對象有兩個作用:
  1)包含一個promise對象(在promise屬性中)。promises是對一個deferred任務執行結果(成功或失敗)的占位符
  2)暴露方法來觸發任務完成(resolve)或者失敗(reject) 
promise API有兩個作用:控制未來任務執行、執行結果。
控制任務執行的entity(在我們的例子中是restaurant)暴露一個promise對象(pizzaOrderFulfillment.promise)給對任務結果感興趣的實體。例子中,Pawel對order感興趣,通過在promise對象上注冊回調函數來表達他的興趣。為了注冊回調函數,使用了then(successCallBack,errorCallBack)方法。這個方法接受回調函數將會在任務結果中調用。為了標志任務執行結束,需要在deferred對象上調用resolve方法。傳遞給resolve方法的參數將會被用來作為提供給success回調函數的值。當success回調函數調用后,任務結束,promise被resolved。通常reject方法調用將會觸發error回調和promise rejection。
 
Promises是first-class js對象
我們可以通過他們(first-class js對象)周圍的參數和函數調用返回它們。這讓我們易封裝異步操作的服務。如下:

restaurant服務封裝了異步任務,從takeOrder方法返回一個promise,返回的promise可以被restaurant顧客使用來放置promised結果,當結果可用時notified。rejecting promises和觸發error回調函數例子如下:
 
收集回調函數
一個promise對象可以被用來注冊多個callbacks,如下所示:
多個成功的callbacks被注冊,當一個promise resolve后所有這些都被觸發,同樣,promise rejection也會觸發所有注冊的error callbacks。
 
注冊callbacks和promise生命周期
一旦promise resolved或者rejected,將不會改變其狀態。僅有一次機會提供promised結果,不會出現以下情況:
  resolve一個rejected promise
  用一個不同的結果resolve一個已經resolved promise
  reject一個resolved promise
  用一個不同的rejection理由reject一個rejected promise
例如如果我們的pizza已經成功送到或者可能吃掉了再告知我們訂單有問題,那就沒有任何意義了。
任何注冊的回調函數在一個promise resolved(rejected)后將會以同樣的結果resolved(或者同樣的失敗緣由rejected)。
 
異步操作鏈
Promise API真正有用的地方是在異步環境中模仿同步函數調用。
繼續pizza例子,想象下現在我們被朋友邀請吃pizza,我們的hosts將會預定一個比薩,一旦order到來,他們將會切塊吃。有一系列異步事件:首先一個pizza需要被delivered,只有這樣才能准備serving。在我們開始享用之前兩個promise需要resolved:restaurant承諾送貨,hosts承諾會給pizza切塊並served。建模如下:
 以上例子中,我們可以看到promises鏈(then方法)。這個結構非常類似於下面的同步代碼:
同時它很容易處理錯誤條件。讓我們來看看錯誤傳播實例:
這里從restaurant得到的rejection結果傳播到對最終結果感興趣的person上。這正是異步環境下的異常處理:拋出的異常將會冒泡給第一個catch塊。
在Promise API,error回調函數作為catch塊,作為標准的catch塊-我們已經得到幾個處理異常情況的選項。我們可以:
  1、recover(從一個catch 塊中返回值)
  2、傳播failure(重新拋出異常)
有Promise API,很容易在catch塊中模擬recovery。例如假定如果需要的pizza不可用,我們的hosts試圖預定另一個pizza
另一種情況是如果recovery不行,我們需要考慮的是重新拋出異常。在這種情況下,唯一的選項是觸發另一個error,$q服務有專用的方法($q.reject):
$ q.reject方法在異步環境中等價於拋出異常。該方法返回一個被拒絕的新的promise。 
 
$q
$q有兩個額外有用的方法:$q.all和$q.when.
聚集promises
$q.all方法使開啟多個異步任務成為可能,當所有任務都完成后notified。從幾個異步actions有效聚集promises,返回一個組合的promise可以作為一個連接點。為了說明$q.all方法的實用性,我們考慮從多個restaurants訂食品的例子。我們要等到兩個訂單都到貨才能享用:
 其中一個action失敗了,promise也會被reject
 
封裝值作為promises
 有時同樣的API需要與從同步和異步actions的結果中獲取。在這種情況下,通常將所有結果都視為異步。$q.when方法可以使其將js對象包裝成promise。
如下所示
 
$q的集成
$q不僅是promise的api實現,也是與AngularJS渲染機制緊密連接的。promises可以直接暴露在scope上,並且只要promise resolved就可以自動渲染,這使我們將promise當成model值。
如下,給定以下模版:
和控制器中的代碼:
Hello,world將會在兩秒后自動渲染,無需人工編程干預。
 
$http
現在,我們已經涉及到多個promises,可以去揭秘從$http 方法調用返回的響應對象。$http調用返回一個對象,這個對象上注冊了success和error回調函數。實際上,返回的對象一個完全成熟的promise,這個promise有兩個額外方便的方法(success和error)。作為任何一個promise,一個從$HTTP調用返回的還具有then方法,允許我們以如下形式重寫callback注冊代碼:
由於$HTTP服務從其方法調用上返回promise,當與后端交互時可以很容易地聚集回調,鏈和連接請求,以及在異步環境下可以使用復雜的錯誤處理。
 
英文原文:Mastering Web Application Development with AngularJS


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM