浏览 3123 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-05-31
最后修改:2010-06-01
采用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方法.這段腳本是將這個鏈接包裝成一個表單,它是對你的代碼進行保護的一種辦法.) 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间: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] |
|
返回顶楼 | |
发表时间:2010-06-11
写的不错 长见识 不过全是繁体的。。。
难道你是台湾人。。 |
|
返回顶楼 | |