Rails之道 ---><The Rails Way> 摘录(4)REST,資源和Rails

1.REST(Representational State Transfer)具備表象狀態轉移
采用REST的主要原因之一是所有Web開發者都要思考怎樣去命名和組織應用程序中的資源和動作.

2. 使用Rails提供的REST支持有兩個優點
[quote]
(1)便利店自動化最佳實踐
(2)開放的應用程序REST服務接口
(備注:實際上Rails的REST支持是一項將劇名路由自動打包的技術 )[/quote]

3.
 map.resources :auc

(1)只需將上面這行代碼加入routes.rb中,它將會自動創建4個具名路由.實際上它讓你能夠鏈接到7個不同的控制器動作上,并且這些動作都具有CRUD近似風格的名字.

(2)調用map.resources :auc引入了一種路由選擇系統的處理機制,它提供指向7個控制器動作的4個具名路由,它們之間通過HTTP請求方法來區分.最后你會得到7個特定名稱的控制器動作:

[img]http://dl.iteye.com/upload/attachment/257610/50c52fa9-6a61-365b-b90c-4bb18acb2ace.jpg[/img]

(3)常見情況整理
[quote]
a.默認的請求方法是GET
b.form_tag和form_for調用的請求方法是POST。
c.在生成URL的時候可以明確指定請求方法(在PUT或DELETE操作時)
[/quote]
eg:(如何指定DELETE操作綁定link到destroy動作)
<%= link_to "Delete this auc", :url => auc(@auc), :method => :delete %>


eg:在輔助方法中使用Hash參數來指定請求方法,這里以form_for方法為例
<% form_for  "auc", :url => auc(@auc),
:html => { :method => :put } do |f|%>


4.重新認識HTTP方法
表單的提交用的是POST方法,而index動作則用GET方法,這就意味著路由選擇系統能辨識下面兩種區別:

/aucs是GET提交的請求

/aucs是POST提交的請求
這是兩回事.我們可以根據情況用不同的HTTP請求方法對同一個URL(aucs)進行請求.

5.Web瀏覽器不懂得處理GET和POST以外的請求方法,所以為了可以發送PUT和DELETE請求,Rails用了一些小技巧,即:

一個PUT或DELETE請求在Rails的REST環境中實際上是POST一個包含_method這個隱藏字段的請求,_method中要么是"put",要么是"delete"。Rails應用程序只會處理這樣的請求,并路由導向合適的update或destroy動作.

6.REST 化的資源有些事單數的,有些事復數的,請往下看------>
[quote]
(1)一個可以show,new.edit和destroy的資源是單數的,因為它們都指向特定的某個資源.

(2)剩下的路由是復數的,也就是處理資源集合的.[/quote]

單數的REST化資源需要用一個參數明確指定操作的資源.這里有兩種形式,
[quote]
(1)直接使用 ---> item_url(@item) #根據HTTP提交方法進行show,update或destroy操作

或者Hash的形式

(2)item_url(:id => @item)[/quote]

(備注:Rails會自動將第一種形式轉換為第二種形式)

7.new和edit會遵從某種特殊的REST化命名規范,其中的原因是為了處理create和update,以及怎樣將new和edit與它們關聯.
[quote]
(1)create和updaet操作一般都是由表單提交進行調用的,這意味著它們確實分別調用了兩個動作(請求)
a.顯示在表單獨結果
b.在表單提交時處理表單輸入
(2)REST化路由選擇的處理方案是create與new關聯,update與edit關聯.new和edit這兩個動作其實屬于輔助動作,它們只是為了展示表單,并未創建或更新資源處理流程的一部分[/quote]

8.單數的資源路由 map.resource (這個單數形式用于表現在環境中只存在一個資源)
map.resource :ad_book
全部單數路由: GET/PUT ad_book_url, GET edit_ad_book_url和put update_ad_book_url

9.嵌套資源

 
map.resources :auctions do |auction|
auction.resources :bids
end


(備注:可以通過:path_prefix選項來顯式聲明資源映射以實現嵌套路由 ==>

map.resources :auctions
map.resources :bids, :path_prefix => "auctions/:auction_id" #為了定位到某個auction的其中一個bid或者所有bid,就要求URL中包含靜態字符串"auctions"和一個auction_id值.

)
(注意在代碼內層里,調用resources方法的是auction而不是map,這一點很容易弄錯.)
<%= link_to "See alll bids", auction_bids_path(@auction)%>
在上面的調用中,路由選擇系統會在/bids的前面加上/auctions/3.而在接收方這邊,這個URL指定的動作bids/index可以通過params[:auction_id]得到@auction的id(這個是用GET進行請求的復數REST化路由)
===> auc/3/bids/5

為什么不跳過auc直接訪問bids/5?有兩個原因--->
[quote]
(1)這個URL包含了很長的信息,但是這是為了提供更多關于資源的信息;

(2)借助Rails中的這種REST化的路由的URL,可以直接通過參數(params[:auction_id])訪問到auction的id[/quote]


10.資源嵌套的深度沒有限制.每多一層的嵌套會讓嵌套路由的參數加一,這意味著單數路由(show,edit和destroy)需要兩個以上的參數
eg:

<%= link_to "Delete this bid",
auction_bid_path(@auction, @bid), :method => :delete%>


12.顯式的設置:name_prefix
有時候需要將某個資源嵌套到多個資源下,有時則需要同時以嵌套的方式和直接訪問這兩種方式來訪問資源.你可能想讓你的具名路由輔助方法依賴于環境來指向不同的資源. :name_prefix選項就能做到這些,它讓你通過控制具名路由輔助方法生成的結果來控制這一切.
如先前通過auction獲得bid的例子,現在情況是需要識別和生成下面兩種URL:
/auctions/3/bids/5和/bids/5
第一個可以用bid_path(auction, @bid)得到,第二個則是bid_path(bid),這樣就可以根據具體邏輯決定是否傳入auction采取嵌套.
 
map.resources :auctions do |auction|
auction.resources :bids, :name_prefix => nil
end

[color=red](Warning:盡量不要在你的應用程序里使用這種技術,因為它會加重調試路由的負擔,也就是說會讓你得不償失....)[/color]

13.你可以在resources方法中使用:controller來明確指出使用什么控制器.這個選項能讓你對資源任意命名,而控制器采用與資源不同的另一套命名,
eg:

map.resources :kaka, :controller => :auctions do |auction|
auction.resources :yuan, :controller => :bids
end

14.DHH建議在合適的時候應該果斷的使用嵌套路由.特別是當一個嵌套路由應當只通過它的父資源訪問時,這在代碼中可以很簡單的通過ActiveRecord的關聯來實現
eg: 使用父對象的has_many關聯來加載一個嵌套資源

@auction = Auction.find(params[:auction_id])
@bid = @auction.bids.find(params[:id])


15.Rails的REST化路由將具名路由和常用的控制器動作集成后打包起來,但有時還需要一些自定義的空間,而且在享受REST化的路由各種優點的情況下.即混合具名路由和HTTP方法.

16.例如,我們可以讓bid可以撤銷,基本的嵌套路由如下所示.

map.resources :auctions do |a|
a.resources :bids
end


我們想要用retract動作來展示一個表單(以及為撤銷做一個過濾).retract不像destroy,是類似destroy之前的一個步驟,這個類似edit動作是update動作之前的一個步驟
/auctions/3/bids/5/retract
還有一個retract_bid_url的輔助方法.要實現這些功能,只需添加:member路由到bid

eg:添加額外的成員路由

map.resources :auctions do |a|
a.resources :bids, :member => { :retract => :get }
end


然后就可以像下面的代碼在視圖中添加一個撤銷的鏈接
<%= link_to "Retract", retract_bid_path(auction,bid) %>

生成URL最后部分就是/retract.我們僅僅提供了一個撤銷表單并沒有包含撤銷過程.因為根據HTTP方法的原則,GET請求時不能修改服務器狀態的,那這時候就應該采用POST請求, 添加一個:method選項到linke_to 方法
<%= link_to "Retract", retract_bid_path(auction,bid), :method => :post %>


看看之前的eg代碼,我們定義了采用GET來請求撤銷路由,所以POST請求就不能被路由選擇系統識別了.解決方法是讓成員路由能接受任意多HTTP方法,如下所示:
map.resources :auctions do |a|
a.resources :bids, :method => { :retract => :any }
end


同樣的,我們可以添加一個應用程序到資源集合的路由,比如終止一次交易
map.resources :auctions, :collection => { :terminate => :any }

這樣就添加了terminate_auctions_path方法,它會產生一個映射到auctions控制器的terminate動作的URL.(我們可以有效地立即終止所有事件)


17.REST有一個規則 ==>一個基于REST的系統應該可以轉換資源的多種表現,所以區分資源的表現形式是很重要的.
作為客戶端或者REST服務的用戶都不必真正從服務器上撤銷一個資源,但可以撤銷一個表現形式.

18.respond_to方法
REST化的Rails實踐中通過控制器的respond_to方法就能依照需要向客戶端返回請求的表現形式.創建資源路由之后,URL會自動識別URL最末端:format參數.

eg:

def index
@page_title ='Listing books'
@book = Book.tag_counts()

sort_by = params[:sort_by]
@books =Book.paginate :page =>params[:page],
:order => sort_by,
:per_page => 10

respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @books }
end
end


現在可因鏈接到: http://localhost:3000/books.xml
這個資源路由會鏈接到index動作,并且識別出.xml的結尾,通過respond_to返回XML表現形式.

(資源路由為具名路由提供了.:format版本,比如說想要一個連接到XML表現形式的資源時,可以使用 formated_版本的REST化的具名路由.
eg:
<%= link_to "XML version of this auction,
formated_auction_path(@auction, "xml") %>

它會生成以下的HTML
<a href="/auctions/1.xml"> XML version of this auction</a>

這個鏈接綁定到auction控制器show動作中respond_to代碼塊的XML分支.
)

19.REST化的Rails動作集合(index,show,destroy,new,create,edit,update)
(1)Index
一般來說,index動作就是提供多個資源或資源集合的表現形式,并且這個表現形式多數是公開和泛化的.index動作一般會展示最常見的一些表現形式.
eg:一個典型的index動作會像下面所示

class AuctionsController < ApplicationController
def index
@auctions = Auction.find(:all)
end
end


視圖模板會顯示每次auction(拍賣)的公開信息,還包含指向每一個對應拍賣的買家信息的鏈接(備注:該書里講的是一個拍賣系統的案例,因此很多代碼都是跟這個有關的)
雖然index公開是很好的,但總有可能會對某些展現的資源集合做出一些限制.舉個例子,用戶可能會去查看自己的bid列表,而你也不可能讓所有人能夠看到除了自己外其他人的bid列表.
最好的策略是對請求的資源集合作過濾,這里可以用REST化路由選擇來實現.
(ff:我認為思考的過程是精華)考慮一下每個用戶查看自己的bid歷史的場景.我們覺得應該根據當前用戶(@user)來過濾bid控制器index動作的資源.這里的問題是怎么排除掉當前用戶以外的其他用戶的資源.如果我們需要查看當前所有最高的bid時,該用什么動作?應該是重定向到auction的index視圖.這里的問題是盡量保持公開性.
這里有兩種解決方法:
一是檢測當前登錄的用戶并基于這個進行展示.但這可能行不通,因為當前用戶可能會想要去查看其他人的公開資源;
二是依賴服務器狀態來過濾結果,這個好像比前一個好.
eg:

map.resources :auctions do |auctions|
auctions.resources :bids, :collection => {:manage => :get}
end


現在我們就能用分層的方法來組織資源了,可以根據條件過濾.

class BidsController < ApplicationController
before_filter :load_auction
before_filter :check_authorization, :only => :manage
def index
@bids = Bid.find(:all)
end
def manage
@bids = @auction.bids
end
...
protected
def load_auction
@auction = Auction.find(params[:auction_id])
end
def check_authorization
@auction.authorized?(current_user)
end
end


這樣就能很好的劃分/bids和/bids/manage在應用程序中扮演的角色.

(2)Show
REST化的show動作是資源的單數狀態的表現.它通常用來表述一個對象,或者一個集合中的某個成員的信息.類似于index動作,show動作也通過GET請求觸發.
eg:

class AuctionsController < ApplicationController
@auction = Auction.find(params[:id])
end

當然show動作可能依賴before_filters 去加載要使用的資源.我們可以根據不同的路由,或者不同的用戶狀態(比如有的用戶具有修改權限,而有的具有特殊信息)在show動作中展示不同的內容.

(3)Destroy
destroy 動作是不能輕易調用的管理動作,它取決于要刪除的對象是什么.你可能需要像下面里所作的那樣把 destroy動作保護起來.
eg:為destroy動作設置安全檢驗
class UserController < ApplicationController
before_filter :admin_required, :only => :destroy
end


典型的destroy動作如下,注意@user已在過濾器中完成了加載.

def destroy
@user.destroy
flash[:notice] = "User deleted!"
redirect_to users_url
end

下面是常見的管理界面的視圖模板:

<h1>Users</h1>
<% @users.each do |user| %>
<p><%= link_to h(user.whole_name), user_path(user) %>
<%= link_to(“delete”, user_path(user), :method => :delete) if
current_user.admin? %></p>
<% end %>

其中刪除鏈接會檢測當前用戶是否為管理員.
實際上關于REST化的刪除是在視圖中包含到動作的鏈接完成的.下面是上面的視圖所生成的HTML代碼.
<p><a href=”http://localhost:3000/users/2”>Emma Knight Peel</a>
<a href=”http://localhost:3000/users/2” onclick=”var f =
document.createElement(‘form’); f.style.display = ‘none’;
this.parentNode.appendChild(f); f.method = ‘POST’; f.action =
this.href;var m = document.createElement(‘input’);
m.setAttribute(‘type’, ‘hidden’); m.setAttribute(‘name’, ‘_method’);
m.setAttribute(‘value’, ‘delete’); f.appendChild(m);f.submit();return
false;”>Delete</a>)</p>


(備注;你可以發現在HTML代碼里有很多JavaScript代碼在兩個鏈接中.原因是DELETE操作是危險的,所以Rails要對點擊這個鏈接的操作進行確認.(假如沒有這個步驟,那如果被網絡爬蟲或機器人抓取并訪問這個鏈接就麻煩大了.)因此需要這么一堆JavaScript來指定DELETE方法.這段腳本是將這個鏈接包裝成一個表單,它是對你的代碼進行保護的一種辦法.)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值