angularjs springMVC 交互

AngularJS中的$resource使用與Restful資源交互

1.AngularJS中的 $resource

 這個服務能夠建立一個資源對象,咱們能夠用它很是方便地同支持RESTful的服務端數據源進行交互,當同支持RESTful的數據模型一塊兒工做時,它就派上用場了。    
  REST是Representational State Transfer(表徵狀態轉移)的縮寫,是服務器用來智能化地提供數據服務的一種方式   
     javascript

  1)咱們首先須要引入ng-Resource  模塊,在angular以後html

  <script src="js/vendor/angular.js"></script> 

  <script src="js/vendor/angular-resource.js"></script>java

 

  2) 在咱們的應用中須要將其當作依賴進行引用 
  angular.module('myApp', ['ngResource']); 
  
  3)如何使用? 
  $resource服務自己是一個建立資源對象的工廠,返回的$resource對象中包含了同後端服務器進行的交互的高層API. 
  
    var User=$resource('/api/users/:userId',{userId:'@id'}); 

    能夠把User對象理解成同RESTful的後端服務進行交互的接口。angularjs

 

【HTTP GET類型的方法】

  ①GET請求:  get(params,successFn,errrorFn)web

   不定義具體的參數,get()請求一般被用來獲取單個資源。ajax

 

   //GET /api/users 
   User.get(function(resp){ 
    //處理成功 
    
   },function(err){ 
    //處理錯誤 
    
   }); 
     
  若是參數中傳入了具名參數(咱們例子中的參數是id),那麼get()方法會向包含id的URL發送請求:    
    //發起一個請求:GET-->/api/users/123 
    User.get({id:'1234'},function(resp){ 
       //success 
    },function(error){ 
       //fail 
    }); 
     
  ②QUERY 請求:query向指定URL發送一個GET請求,並指望返回一個JSON格式的資源對象集合。 
  //發起一個請求 
  User.query(function(users){ 
    //讀取集合中的第一個用戶 
    var user=users[0]; 
  }); 
     
  query()和get()方法之間惟一的區別是AngularJS指望query()方法返回數組。   

 

   

【非HTTP GET類型的方法】     

  1. save(params, payload, successFn, errorFn) 
  save方法向指定URL發送一個POST請求,並用數據體來生成請求體。save()方法用來在服務器上生成一個新的資源。 payload:表明請求發送的數據體 

   //發送一個請求 with the body {name: 'Ari'}spring

   User.save({},{name:'Ari'},function(resp){ 
    
   },function(error){ 
    
   }); 
     
 2. delete(params, payload, successFn, errorFn)    
 delete方法會向指定URL發送一個DELETE請求,並用數據體來生成請求體。它被用來在服務器上刪除一個實例:    
     
    // DELETE /api/users 
    User.delete({}, { 
        id: '123' 
    }, function(response) { 
    // 處理成功的刪除響應 
    }, function(response) { 
    // 處理非成功的刪除響應 
    }); 
     
  3. remove(params, payload, successFn, errorFn) 
    remove方法和delete()方法的做用是徹底相同的,它存在的意義是由於delete是JavaScript的保留字,在IE瀏覽器中會致使額外的問題。   
     
    // 發起一個請求: 
    // DELETE /api/users 
    User.remove({}, { 
     id: '123' 
    }, function(response) { 
    // 處理成功的刪除響應 
    }, function(response) { 
    // 處理非成功的刪除響應 
    }); 

2.$resource Restful api 與 ngResoruce

      $http服務提供了一個很是低級的實現,能夠用來發送XHR請求,同時它還爲你提供了很大的可控性和靈活性。可是,在大多數狀況下,咱們須要處理對象,以及封裝了特定屬性和方法的對象模型,例如一個person對象(帶有詳細信息),或者一個信用卡對象。shell

        在這些狀況下,若是咱們可以建立一個JS對象,並且它能夠理解並表明這種對象模型,是否是會很棒?若是咱們僅僅編輯這個對象的屬性,例如保存或者更新,那麼這些狀態會被持久化到服務端嗎?數據庫

        $resource就是爲這一功能而設計的。AngularJS中的resource(資源)容許咱們用描述性的方式來定義對象模型,它能夠描述如下內容:c#

        1.資源在服務端的URL。

        2.經常使用的請求參數類型。

        3.一些附加的方法(你能夠自動得到get、save、query、remove和delete方法),這些方法爲對象模型包裝了特定的功能和業務邏輯(例如信用卡對象的charge()方法)。

        4.指望得到的響應類型(一個數組或者一個對象)。

        5.協議頭。

        使用Angular所提供的$resource對象,你能夠根據各類需求查詢服務器;除此以外,你還能夠把服務端返回的對象當成已經持久好的數據模型,你能夠修改它們,而且能夠把它們持久化。

        ngResource是一個獨立的、可選的模塊。爲了使用它,須要:

a.在加載的腳本文件中包含angular-resource.js

b.在模塊依賴聲明中包含ngResource(例如,angular.module('myModule', ['ngResource']))。

c.在須要的地方使用注入的$resource服務。

        在學習如何使用ngResource方法建立資源以前,咱們先來看看使用基本的$http服務建立相似的東西須要作些什麼事情。對於咱們的信用卡資源來講,除了要可以對它進行"change"(收費)操做以外,咱們還要可以get(獲取)、query(查詢)以及save(保存)信用卡。

        如下是一種可能的實現:

myAppModule.factory('CreditCard', ['http', function($http) {
 var baseUrl = '/user/123/card';
 return {
 get: function(cardId) {
 return $http.get(baseUrl + '/' + cardId);
},
 save: function(card) {
 var url = card.id ? baseUrl + '/' + card.id : baseUrl;
 return $http.post(url, card);
},
 query: function() {
 return $http.get(baseUrl);
},
 charge: function(card) {
 return $http.post(baseUrl + '/' + card.id, card, {params: {charge: true}});
}
};
}]);

    除了這種方式以外,還能夠簡單地建立一個Angular服務,這個服務將會經過如下方式來描述應用所提供的資源:

 myAppModule.factory('CreditCard', ['$resource', function($resource) {
 return $resource('/usr/:userId/card/:cardId',
 {userId: 123, cardId: '@id'},
 {charge: {method: 'POST', params: {charge: true}, isArray: false});
}]);

     如今,只要向咱們AngularJS注射器請求一個CreditCard實例,咱們就能夠獲取一個Angular資源,它默認爲咱們提供了一些基礎的方法。下表列出了這些方法的內容以及它們的行爲,有了這些信息你就知道應該如何配置服務端了。         下面咱們來看一個信用卡的實例,這會讓咱們的思路更加清晰。

//假設CreditCard服務被注入到了這裏
//咱們能夠從服務端獲取一個集合,請求的路徑爲GET:/user/123/card
 var cards = CreditCard.query();
//咱們還能夠在回調函數中獲取並使用單張信用卡 CreditCard.get({cardId: 456}, function(card) { //每一個實例都是CreditCard類型  expect(card instanceof CreditCard).toEqual(true);  card.name ="J.Smith"; //非GET型的方法被映射到了實例上 card.$save(); //咱們自定義的方法也被映射上去了 card.$charge({amount:9.99}); //發起一個POST請求:/user/123/card/456?amount=9.99&charge=true //發送請求時傳遞的數據爲:{id:456, number: '1234', name: 'J.Smith'} });

   這個例子涉及了比較多的內容,對於其中比較重要的內容依次介紹以下:

一.聲明

        不管是自已定義$resource,仍是使用正確的參數來調用注入的$resource函數,操做都很是簡單。

        $resource函數有一個必需的參數,便可用資源的URL地址,還有兩個可選的參數,即默認參數以及你想配置在資源上的額外動做。

        請注意URL是參數化的(用:來標識參數。:userId表示userId將會被替換成對應的文本,:cardId表示將會被cardId實參的值替換掉)。若是沒有傳遞參數,對應的標識符會被替換成空字符串。

        第二個參數負責處理每個請求中都會被髮送的默認值。在當前這個例子中,咱們會把常量123傳遞給userId。參數cardId更加有趣,「cardId是"@id."」表示的是,若是咱們正在使用一個從服務端返回的對象,那麼當調用這個對象上的任意方法時(例如調用對象的$save方法),對象上的id屬性值就會被賦給cardId參數。

        第三個參數是另外一個函數,咱們但願在自定義的資源上暴露這個函數。

二.自定義方法

        調用$resource時,傳遞的第三個參數是一個可選的。咱們但願在自已的資源上暴露的方法。

        在前面的例子中,咱們指定了一個charge方法,能夠經過傳遞一個對象來配置這個方法,對象中的key就是須要暴露的方法名稱。配置項中須要指定的內容有:請求的類型(GET、POST等)、須要做爲請求的一部分來傳遞的參數(在這個例子中就是charge=true),以及返回的結果是不是一個數組(在這個例子中不是)。一旦作完這些事情以後,你就能夠自由地調用CreditCard.charge()了

        說明:這是一種很是靈活的編碼風格,根據上面的代碼,對於配置對象{charge: {method: 'POST', params: {charge: true}, isArray: false},Angular會將其解析成一個方法,而後把這個方法綁定到返回的Restful對象上,上面的配置對象解釋以後的方法爲:

CreditCard.charge = function(charge, isArray) {
//這裏是方法體
}

三.別用回調!(除非你真的須要它們)

      第三個須要注意的內容是調用資源時的返回值類型。請再看一下CreditCard.query()調用,咱們直接把信用卡對象賦值給了card變量,而並無在回調函數裏面進和賦值。你可能會擔憂在對服務器進行異步請求的狀況下,這種代碼能運行嗎?

     你這種擔憂是合理的。但事實上,這段代碼徹底正確,而且可以運行。這裏發生的事情是,AngularJS賦給了card對象一個引用(一個對象或者數組,具體是什麼須要根據所指望的返回值類型而定),在將來的某個時間上,當對服務器的請求返回來以後,這個引用纔會被真正賦值。在些期間,引用對象一直是空的。

      對於AngularJS應用來講,最多見的處理流程是:到服務器上獲取數據,而後把數據賦值給變量,再把數據顯示到模板中。這種快捷方式是很是好用的。在控制器代碼中,你惟一要作的事情就是發起對服務端的調用,把返回值賦給正確的做用域變量,而後讓模板自動負責渲染它。因爲card變量是使用{{}}這種數據綁定技術綁定到視圖上的,因此一開始給它一個空值並無問題,等異步響應返回以後再把結果賦給它。這時候Angular的數據綁定機制會當即發現數據發生了變化,而後會自動通知視圖進行刷新。從這裏能夠看到,使用Angular框架時,對異步調用的不少處理方式已經發生了細微的變化。

        若是你有一些須要依賴於返回值才能執行的業務邏輯,那麼這種方法就不會奏效。在這種狀況下,你就須要使用回調函數,這個回調函數會在調用CreditCard.get()的時候被使用。

四.簡化服務端操做

     不管你使用返回值的快捷方式,仍是使用回調函數,都有一些關於返回對象的注意事項。返回值不是普通的JS對象,而是一個"resource"型的對象。這就意味着,除了服務端返回的數據以外,它上面還帶有一些附加的行爲(在這個例子中就是$save()和$charge())。這樣可讓你更容易進行服務端調用,例如獲取數據、修改數據,以及把修改的內容持久化到服務端(也就是在不少應用中都很常見的CRUD操做)。

五.什麼時候可使用Angular資源

       只有服務端按照RESTful的方式工做的時候,你纔可使用Angular資源。對於信用卡場景,它須要:

        1.一個到/user/123/card的GET請求,它會返回用戶123的信用卡列表。

        2.一個到/user/123/card/15的GET請求,它會返回用戶123的ID爲15的信用卡。

        3.一個到/user/123/card的POST請求,在POST的數據中帶有信用卡信息,它將會爲用戶123的ID建立一張新的信用卡。

        4.一個到/user/123/card/15的POST請求,POST的數據中帶有信用卡信息,它將會更新用戶123的ID爲15的信用卡信息。

        5.一個到/user/123/card/15的DELETE請求,它將會刪除用戶123的ID爲15的信用卡信息。

     我發現一個Angular JS中的關鍵問題是(以我喜歡的代碼工做方式來講)$save方法在ngResource中將只會使用POST沿着有效載荷提交到服務器。新建和更新記錄操做都是這樣的,對來自服務器的新和舊的對象都是如此。這破壞了 RESTful約定的更新操做應該使用PUT或者PATCH操做。我下面建議的解決方案拓展了現有的ngResource實現,提供了更多的默認選項,同時精簡了咱們的工做流程。完美的用法(恕我直言)應該像下面這樣:

1
2
3
4
5
6
7
var  user =  new  User;
user.name =  'Kirk Bushell' ;
user.$save();  // POST
 
var  user = User.get( { id: 1 });
user.name =  'John smith' ;
user.$save();  // PUT

    若是咱們深刻ngResource的代碼中,這樣的需求是可能的,關於怎麼樣去簡化它的實現(這應該是由 Angular 的團隊來完成)。不幸的是,它的確意味着若是咱們想要同時用POST/PUT來實現保存操做,咱們不得不用兩個不一樣的方法(這不是個人風格)。恕我直言,保存就是保存 --- 讓你的模塊/類 來定義這是什麼樣的保存(新建或是更新)操做。咱們須要作的是用咱們本身的默認實現來拓展ngResource提供的 $resource工廠。讓咱們接着看下去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var  module = angular.module(  'my.resource' , [  'ngResource'  ] );
 
module.factory(  'Resource' , [  '$resource' function ( $resource ) {
    return  function ( url, params, methods ) {
     var  defaults = {
       update: { method:  'put' , isArray:  false  },
       create: { method:  'post'  }
     };
     
     methods = angular.extend( defaults, methods );
 
     var  resource = $resource( url, params, methods );
 
     resource.prototype.$save =  function () {
       if  ( ! this .id ) {
         return  this .$create();
       }
       else  {
         return  this .$update();
       }
     };
 
     return  resource;
   };
}]);

    這裏咱們定義了一個自定義模塊 - my.resource,這個模塊能夠被注入到其餘你想要這個拓展功能的模塊中。咱們接着以一個依賴爲咱們的Resource工廠注入$resource,並作一些小魔法,讓咱們研究下吧。

    首先,咱們定義了一個新的默認數組。它包括了爲resource的更新update和新建create方法 - create方法將會被定義成一個POST請求,update方法將會被定義成一個PUT請求。咱們爲何會想要這兩個額外的方法?由於它容許咱們作更明確的請求,正因如此,咱們須要重載$save方法!

    咱們拓展了任何咱們會提供給resource的方法。而後,咱們定義咱們的新resource和經過重載$save方法拓展它。這個方法會檢查id字段是否包含在一個資源對象中,若是有id字段,它將會調用咱們定義在default中的$update方法;若是沒有id字段,它會調用$create方法,很簡單吧!

    可是 - 咱們怎麼在咱們本身的資源中使用它呢?小菜一碟。

1
2
3
4
5
var  module = angular.module(  'services' , [  'my.resource'  ] );
 
module.factory(  'User' , [  'Resource' function ( $resource ) {
   return  $resource(  'users/:id' , { id:  '@id'  } );
}]);

    如今你能夠看到 - 咱們對待它就像對待其餘的資源同樣注入,惟一的區別是 - 咱們定義了咱們的 $resource依賴於咱們本身進行拓展ngResource 後的Resource。

3.AngularJS Resource 與 Restful API的交互

       REST(表徵性狀態傳輸,Representational State Transfer)是Roy Fielding博士在2000年他的博士論文中提出來的一種軟件架構風格。RESTful風格的設計不只具備更好的可讀性(Human Readable),並且易於作緩存以及服務器擴展(scalability)。REST風格體如今URL設計上:

  • 每一個URL對應一個資源
  • 對資源的不一樣操做對應於HTTP的不一樣方法
  • 資源表現形式(representation)經過AcceptContent-Type指定

    AngularJS提供了$resourceService來更方便地與RESTful服務器API進行交互,能夠方便地定義一個REST資源,而沒必要手動全部的聲明CRUD方法。

   參考文檔: https://docs.angularjs.org/api/ngResource/service/$resource

Resource Factory

$resourceService定義在ngResourceModule中,須要在你的HTML中引入這個Module對應的JS,同時在你的APP中添加這樣一個依賴:

var app = angular.module('helloApp, ['ngResource']); 

而後爲資源創建一個Factory:

app.factory('Notes', ['$resource', function($resource) { return $resource('/notes/:id'); }]); 

固然,你也能夠不把$esource的實例放到Factory裏,直接在控制器中存起來:var Notes = $resource('/notes/:id)

CRUD

在你的控制器中就能夠對資源進行增刪改查了:

app.controller('NotesCtrl', ['$scope', 'Notes', function($scope, Notes) { var notes = Notes.query(function(){ // GET: /notes // Response: [{id: 1, content: 'hello'}, {id: 2, content: 'world'}]; var first = notes[0]; first.content = 'halo'; first.$save(); // POST: /notes/1 {id: 1, content: 'halo'} // Response: {id: 1, content: 'halo'} second.$delete(); // DELETE: /notes/2 }); var note = new Notes({content: 'xxx'}); note.$save(); // POST: /notes // Response: {id: 3, content: 'xxx'} }]); 

PUT 操做

$resource提供了五種默認操做:getquerysaveremovedelete。你能夠配置一個update操做來完成HTTP PUT:

app.factory('Notes', ['$resource', function($resource) { return $resource('/notes/:id', null, { update: { method:'PUT' } }); }]); 

如今,你能夠在控制器中獲取一個note並更新它:

var note = Notes.get({ id: 3}), $id = note.id; note.content = 'yyy'; Notes.update({ id:$id }, note); // PUT /notes/3 {id: 3, content: 'yyy'} 

如今你的Notes有六種操做了。這些操做有兩種調用方式:

  1. 經過資源類調用,例如:Notes.update({id: xxx})
  2. 經過資源實例調用,例如:note.$update(),此時操做名需加前綴$

具體的調用參數可參考文檔:

HTTP GET "class" actions: Resource.action([parameters], [success], [error])

non-GET "class" actions: Resource.action([parameters], postData, [success], [error])

non-GET instance actions: instance.$action([parameters], [success], [error])

其中,success參數爲(value, responseHeaders),error參數爲(httpResponse)

屬性/URL映射

上述例子中,咱們看到note對象的id屬性會映射到URL中的:id/notes/:id)。若是你的業務更加複雜,能夠手動配置這個映射關係。例如:

var Notes = $resouce('/users/:userId/notes/:noteId', { noteId: '@id', userId: '@owner' } 

將會讀取noteownerid屬性來生成URL,好比刪除note時:

// note === {id: 123, owner: 'alice', content: 'hello'} note.$delete(); // DELETE: /users/alice/notes/123 

在構造$resource時,多於的屬性映射會成爲URL Query。例如:

var Notes = $resouce('/notes/:id', { id: '@id', user: '@owner' }); // note === {id: 123, owner: 'alice', content: 'hello'} note.$delete(); // DELETE: /notes/123?user=alice 

REST操做的聲明和調用中,多於的屬性會成爲URL Query。例如:

var Notes = $resouce('/notes/:id', {id: '@id'}, { update: {method: 'PUT', operator: 'bob'} }); // note === {id: 123, content: 'hello'} note.$update({trusted: true}); // PUT: /notes/123?operator=bob&trusted=true {id: 123, content: 'hello'} 

響應轉換

有時基於既定的後臺設計,沒法提供徹底RESTful的API,好比/notes返回的是一個分頁器對象,而非數組。此時,咱們仍然可使用$resource,但須要設置響應轉換回調。例如:

var Notes = $resouce('/notes/:id', null, { pager: { method: 'GET', transformResponse: function(data, headers){ // Server respond: // data = {currentPage: 1, // totalPage: 20, // pageSize: 2, // content: [{id: 1, content: 'hello'}, {id: 2, content: 'world'}]} var pager = JSON.parse(data); return pager.content; } } }); var notes = Notes.query(function(){ // GET: /notes // notes === [{id: 1, content: 'hello'}, {id: 2, content: 'world'}] }); 

相似響應重寫,你還能夠設置請求轉換transformRequest

雖然$resource的設計能夠支持絕大多數的URL和內容表示設計,但若是你發現$resource的使用過程極其複雜,那多是你的服務器API並不知足RESTful風格。

 

 

 
發表評論
 

0個評論

     

    我要留言 ×

    技術領域:

    我要留言 ×

    留言成功,咱們將在審覈後加至投票列表中!

    收藏提示 ×

    本條知識內容已成功收藏到對應的知識圖譜中了!管理個人知識圖譜

    你已經自動關注本知識庫了哦!

    ×
    取消收藏
    肯定要取消收藏嗎?

    提示 x

    AngularJS知識庫已成功保存至 個人結構圖 如今你能夠用它來管理本身的知識內容了

     
    相關文章
    相關標籤/搜索