原文:http://blog.csdn.net/he90227/article/details/50525836
1.AngularJS中的 $resource
這個服務可以創建一個資源對象,我們可以用它非常方便地同支持RESTful的服務端數據源進行交互,當同支持RESTful的數據模型一起工作時,它就派上用場了。
REST是Representational State Transfer(表征狀態轉移)的縮寫,是服務器用來智能化地提供數據服務的一種方式
1)我們首先需要引入ng-Resource 模塊,在angular之后
<script src="js/vendor/angular.js"></script>
<script src="js/vendor/angular-resource.js"></script>
2) 在我們的應用中需要將其當做依賴進行引用
angular.module('myApp', ['ngResource']);
3)如何使用?
$resource服務本身是一個創建資源對象的工廠,返回的$resource對象中包含了同后端服務器進行的交互的高層API.
var User=$resource('/api/users/:userId',{userId:'@id'});
可以把User對象理解成同RESTful的后端服務進行交互的接口。
【HTTP GET類型的方法】
①GET請求: get(params,successFn,errrorFn)
不定義具體的參數,get()請求通常被用來獲取單個資源。
//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'}
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對象(帶有詳細信息),或者一個信用卡對象。
在這些情況下,如果我們能夠創建一個JS對象,而且它可以理解並代表這種對象模型,是不是會很棒?如果我們僅僅編輯這個對象的屬性,例如保存或者更新,那么這些狀態會被持久化到服務端嗎?
$resource就是為這一功能而設計的。AngularJS中的resource(資源)允許我們用描述性的方式來定義對象模型,它可以描述以下內容:
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)通過
Accept
和Content-Type
指定
AngularJS提供了$resource
Service來更方便地與RESTful服務器API進行交互,可以方便地定義一個REST資源,而不必手動所有的聲明CRUD方法。
參考文檔: https://docs.angularjs.org/api/ngResource/service/$resource
Resource Factory
$resource
Service定義在ngResource
Module中,需要在你的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
提供了五種默認操作:get
, query
, save
, remove
, delete
。你可以配置一個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
有六種操作了。這些操作有兩種調用方式:
- 通過資源類調用,例如:
Notes.update({id: xxx})
; - 通過資源實例調用,例如:
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' }
將會讀取note
的owner
和id
屬性來生成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風格。
摘自: http://harttle.com/2015/06/05/angular-resource.html
4.AngularJS 使用 ngResource、Restful API 和Spring MVC 交互
本文為開發者呈現了一些概念和相關的示例代碼,介紹了用ngResource($resource)服務POST方式提交數據到和服務器端SpringMVC環境下的RESTFul APIs。示例代碼可以在如下頁面找到:http://hello-angularjs.appspot.com/angularjs-restful-apis-post-method-code-example。相對於使用$http服務,我更喜歡這種方法的主要理由是ngResource允許你使用抽象方式(例如$resource類),你可以使用它的實例上的處理方法與RESTFul APIs交互。這樣就可以簡單方便地實現RESTFul集成。在$resource類的對象上,可以直接調用處理方法(例如get、save等)。因此,在其實例上,就可以使用"$"作為前綴直接調用這些方法。具體的例子如下所示。 |
![]() kimmking
|
這篇文章里,用以下兩個情景用例來解釋:
代碼片段包含了AngularJs代碼和spring MVC代碼,以能夠讓你簡單快速的上手。 想要$resource 服務工作,需要添加一段實際代碼: 應用angular-resource.js文件,你可以使用Google Hosted Libraries來實現。 下面采用的代碼是最新的angularJs版本。(下面就是引入服務的代碼)
下面的代碼告訴你如何在創建控制器時引入ngResource模塊和注入$resource服務:
保存/持久化新對象 (其實就是傳給后台存進數據庫的一個過程) 下面的代碼演示了如何使用POST方法提交form表單中的user信息(這部分是由controller來做),controller會把uers信息提交給REST URL “/user/new”(這部分是Spring MVC的控制器執行)。 |
![]() 神棍局王某
|
AngularJS代碼
Spring MVC 代碼 請注意User對象的字段要和JSON數據的要一致。同時確保Jackson包已經引入了,並且正常工作了。這是最重要的步驟。我推薦參考這篇文章 how to fix 415 Unsupported Mediatype error 來幫助你實現前面兩個步驟。(1.Spring轉對象的時候,是按照字段名來轉的,比如你的Java的User對象的firstname會綁定Json對象的firstname,所以需要保持一致,否則幫出來的數據可能不對。2.不引人Jackson包,那么Json對象和Java對象不能想換轉化,也就不能正常工作了)
更新已存在的數據對象 下面的代碼演示了如何通過POST方法提交表單信息來更新user對象,請求會發送到服務器的REST URL "/user/{id}",也包括Spring MVC的方法。 |
![]() 神棍局王某
|
AngularJS代碼
Spring MVC 代碼 請注意下面幾點 -用例的路徑變量(就是"/user/{id}"這東西) -Java的User對象要和Json對象匹配(字段名,或者說是屬性名) -確保Jackson包引入並且正常工作(確保你后台能正常轉化Json和java對象)
|
![]() 神棍局王某
|