同外界通信:XHR和服務器通信——Restangular

Restangular簡介

Restangular是一個專門用來從外部讀取數據的AngularJS服務

  • Restangular支持promise模式的異步調用
  • Restangular支持所有的HTTP方法
  • Restangular並不需要事先知道URL或提前指定它們(除基礎URL外)
  • Restangular可以直接處理嵌套的資源,無需創建新的Restangular實例
  • 使用過程中僅需要創建一個Restangular資源對象的實例

Restangular對像簡介

Restangular依賴Lo-Dash或Underscore,因此為了確保Restangular可以正常運行,需要引入這兩個庫中的一個

可以為拉取數據的對象設置基礎路由,會讓所有的HTTP請求將該路徑作為根路徑來拉取數據:

var User = Restangular.all( 'users' );
var allUsers = User.getList(); // GET /users 

也可以通過單個對象來發送嵌套的請求:

var oneUser = Restangular.one( 'users' , 'abc123' );
oneUser.get() // GET /users/abc123
.then( function (user) {
    user.getList( 'inboxes' ); // GET /users/abc123/inboxes
}); 

還可以通過allUrl()和oneUrl()指定請求的URL

使用Restangular

可以使用getList()來獲取所有信息, getList()方法返回了一個集合,其中包含了可以用來操作特定集合的方法

Restangular返回增強過的promise對象,我們可以調用promise對像上的方法,還可以調用一些特殊的方法,比如$object 。 $object會立即返回一個空數組(或對象),在服務器返回信息後,數組會被用新的數據填充:

messages.post(newMessage)
.then( function (newMsg) {
    $scope.messages = messages.getList().$object;
}, function (errorReason) { // 出现了一个错误
}); 

注意,在修改一個對象之前對其進行複制,然後對複制的對象進行修改和保存是一個好做法。 Restangular有自己的複製版本,因此無需對一系列方法重新進行綁定。 在更新對象時使用Restangular.copy()是一個比較好的實踐。

支持所有HTTP方法

Restangular支持所有的HTTP方法:

  • GET: .get()和.getList('books')
  • PUT: .put()
  • POST: .post()
  • DELETE: .remove()
  • HEAD: .head()
  • TRACE: .trace()
  • OPTIONS: .options()
  • PATCH: .patch()

自定義查詢參數和頭

可以傳入參數和頭對象:

var queryParamObj = { role: 'admin' }, 
     headerObj = { 'x-user' : 'admin' };
messages.getList( 'accounts' , queryParamObj, headerObj); 

設置Restangular

將RestangularProvider注入到.config()函數中,或者將Restangular注入到一個.run()函數中,對Restangular進行設置

設置baseUrl

通過.setBaseUrl()方法給所有後端API請求設置baseUrl

angular.module( 'myApp' , [ 'restangular' ])
.config( function (RestangularProvider) {
    RestangularProvider.setBaseUrl( '/api/v1' );
}); 

添加元素轉換

使用elementTransformers可以在Restangular對像被加載後為其添加自定義方法

angular.module( 'myApp' , [ 'restangular' ])
.config( function (RestangularProvider) {
    RestangularProvider.addElementTransformer( 'authors' , false , function (element) {
         element.fetchedAt = new Date ();
         return element;
    });
}); 

還可以對已有數據模型或集合進行擴展: 

angular.module( 'myApp' , [ 'restangular' ])
.config( function (RestangularProvider) {
    RestangularProvider.extendModel( 'authors' , function (element) {
        element.getFullName = function () {
            return element.name + ' ' + element.lastName;
        };
        return element;
    });
}); 

設置響應攔截器

Restangular可以設置響應攔截器。 responseInterceptors在每個響應從服務器返回時被調用。 調用時會傳入以下參數:

  • data:從服務器取回的數據。
  • operation:使用的HTTP方法。
  • what:所請求的數據模型。
  • url:請求的相對URL。
  • response:完整的服務器響應,包括響應頭。
  • deferred:請求的promise對象。

angular.module( 'myApp' , [ 'restangular' ])
.config( function (RestangularProvider) {
    RestangularProvider.setResponseInterceptor( function (data, operation, what) { //getList方法应当始终返回数组
        if (operation == 'getList' ) {
             var list = data[what];
             list.metadata = data.metadata;
             return list;
        }
        return data;
    });
}); 

getList方法應當始終返回數組

設置請求攔截器

requestInterceptors在將數據實際發送給服務器之前對其進行操作。 使用setRequestInterceptor()來設置requestInterceptor。 這個方法可以接受下面四個參數:

  • element: 發送給服務器的資源
  • operation: 所使用的HTTP方法
  • what: 所請求的數據模型
  • url: 所請求的相對URL

angular.module( 'myApp' , [ 'restangular' ])
.config( function (RestangularProvider) {
    RestangularProvider.setRequestInterceptor( function (elem, operation, what) {
        if (operation === 'put' ) {
            elem._id = undefined;
            return elem;
        }
        return elem;
    });
}); 

自定義字段

Restangular支持自定義字段,可以通過.setRestangularFields()方法來自定義:

angular.module( 'myApp' , [ 'restangular' ])
.config( function (RestangularProvider) {
    RestangularProvider.setRestangularFields({
        id: '_id.$oid'
    });
}); 

通過errorInterceptors來捕獲錯誤

通過設置錯誤攔截器可以捕獲Restangular內的錯誤。 通過errorInterceptor可以將錯誤信息在應用中進行傳遞

angular.module( 'myApp' , [ 'restangular' ])
.config( function (RestangularProvider) {
    RestangularProvider.setErrorInterceptor( function (resp) {
        displayError();
        return false ; // 停止promise 鏈
    });
}); 

孤立資源設置

如果我們想加載一個沒有嵌套在其他資源中的資源,可以使用.setParentless()設置告訴Restangular不要構造嵌套結構的URL

angular.module( 'myApp' , [ 'restangular' ])
.config( function (RestangularProvider) {
    RestangularProvider.setParentless([ 'cars' ]);
}); 

.setParentless()接受兩種類型的參數:

  • true:所有的資源都會被當作孤立資源處理,沒有任何URL會進行嵌套
  • 數組:只有定義在這個數組中的資源會當作孤立資源處理,數組的元素是字符串,字符串的值是資源的標識

使用超媒體

在實踐中,只通過一個切入點(主URL)來同後端服務器進行通信是非常好的做法,其他數據模型通過鏈接來指向相關聯的資源。

Restangular通過selfLink 、 oneUrl和allUrl來支持這個有用的做法。

自定義Restangular服務

強烈建議將Restangular封裝在一個自定義服務對象內