`
fireflyman
  • 浏览: 112882 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

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

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

2.  使用Rails提供的REST支持有兩個優點
  
引用

   (1)便利店自動化最佳實踐
   (2)開放的應用程序REST服務接口
   (備注:實際上Rails的REST支持是一項將劇名路由自動打包的技術 )


3.
 map.resources :auc

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

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



(3)常見情況整理
 
引用

   a.默認的請求方法是GET
   b.form_tag和form_for調用的請求方法是POST。
   c.在生成URL的時候可以明確指定請求方法(在PUT或DELETE操作時)

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 化的資源有些事單數的,有些事復數的,請往下看------>
引用

(1)一個可以show,new.edit和destroy的資源是單數的,因為它們都指向特定的某個資源.

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


單數的REST化資源需要用一個參數明確指定操作的資源.這里有兩種形式,
引用

(1)直接使用 ---> item_url(@item)  #根據HTTP提交方法進行show,update或destroy操作

或者Hash的形式

(2)item_url(:id => @item)


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

7.new和edit會遵從某種特殊的REST化命名規范,其中的原因是為了處理create和update,以及怎樣將new和edit與它們關聯.
   
引用

    (1)create和updaet操作一般都是由表單提交進行調用的,這意味著它們確實分別調用了兩個動作(請求)
      a.顯示在表單獨結果
      b.在表單提交時處理表單輸入
     (2)REST化路由選擇的處理方案是create與new關聯,update與edit關聯.new和edit這兩個動作其實屬于輔助動作,它們只是為了展示表單,并未創建或更新資源處理流程的一部分


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?有兩個原因--->
引用

(1)這個URL包含了很長的信息,但是這是為了提供更多關于資源的信息;

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



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

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

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方法.這段腳本是將這個鏈接包裝成一個表單,它是對你的代碼進行保護的一種辦法.)





  • 大小: 25.6 KB
分享到:
评论
2 楼 hedahai119 2010-06-11  
写的不错 长见识 不过全是繁体的。。。
难道你是台湾人。。
1 楼 fireflyman 2010-06-05  
def map.controller_actions(controller, action)
 actions.each do |action|
   self.send("#{controller}_#{action}", "#{controller}/#{action}", :controller => controller, :action => action)
 end
end
map.controller_actions 'about', %w[company privacy license]

相关推荐

Global site tag (gtag.js) - Google Analytics