在一個基於MVC的Web應用程序中,應用程序邏輯以及數據的存取是由MVC中的C,也就是控制器來完成的。因此,深刻地理解MVC框架所提供控制器對於開發一個高效、可升級、穩定的應用程序是十分必要的。RoR也不例外。
RoR中所提供的控制器叫動作控制器(ActionController)。本文將主要討論動作控制器所提供的幾種服務,以及如何使用動作控制器。
什麼是動作控制器
在RoR中,動作包(Action Pack)是這個框架的核心。它包括兩部分,動作視圖和動作控制器。動作包的一個特點是除了Web程序,不能使用在其它類型的程序中。下面讓我們看看在我們通過浏覽器鍵入一個URL後,如http://localhost:3000/demo/say/hello,都發生了什麼。下面是在RoR中處理動作的步驟:
1. RoR首先裝載了位於app/controllers目錄中的say_controller.rb文件。這個文件只被裝載一次。
2. 然後 RoR建立了類SayController的實例。
3. 一旦SayController類被實例化,RoR就會在app/helpers中查找say_helper.rb文件。如果這個文件存在,它就會被裝載,並且這個文件將會和SayController對象混合。這就意味著在SayController對象中可以直接訪問SayHelper中的方法。
4. 最後在app/models中查找say.rb文件,如果存在,裝載它。
到現在為止,我們已經對應用程序的初始化過程非常清楚了,接下來讓我們繼續看看動作控制器所提供的服務。下面是RoR所提供的服務列表:
1. URL映射
2. 會話跟蹤
3. 過濾和驗證
4. 緩沖
現在又帶來一個問題。這些服務為什麼由控制器來提供。當然,答案也很簡單,這是因為控制器介於數據和應用程序之間,因此,它可以監視數據的存取,並且可以根據需要對URL進行映射。因此,這些服務理所當然由控制器來提供。下面我們將詳細討論控制器提供的這些服務。
1. URL映射
當我們在浏覽器中輸入http://localhost:3000/admin/show時,會顯示相應的內容。但你也許會有疑問,RoR是如何將URL鏈接映射成相應的類或方法呢?事實上,這些映射的代碼都被寫在了config目錄中的routers.rb中。下面是這個文件的部分代碼。
ActionController::Routing::Routes.drawdo|map| map.connect ':controller/service.wsdl', :action => 'wsdl' map.connect ':controller/:action/:id' end
動作控制器通過它的映射組件將來自外部請求的URL和內部的應用程序連接了起來。上述代碼的第3行就是完成這個功能的。在這行語句中,map.connect的連接字符串是":controller/:action/:id"。請求的URL只有匹配這個字符串才能被接受。對於一個URL請求來說,它可以被RoR分成三部分:
a. 第一部分是模式字符串中的:controller部分。
b. 第二部分是模式字符串中的:action部分。
c. 第三部分是模式字符串中的:id部分。
根據上面所描述的三部分,URL:http://localhost:3000/demo/admin/show/1/將被映射成以下三部分:
:controller : 'admin', :action :'show', :id :1
根據以上的三部分,RoR將調用admin控制器的show方法,並將參數1傳到show方法中。因此,我們可以看出,RoR在其中做了很多本應該由我們做的事件。因此,RoR是一項十分強大技術。
2. 會話跟蹤
跨應用跟蹤用戶是大多數Web應用程序都需要的功能。在RoR中,我們可使用由RoR框架提供cookies或session管理來跟蹤用戶。但在RoR中的cookies和session管理和其它框架所提供的類似的管理不同的,RoR的cookies和session管理無需顯式地調用相應的cookies和session對象就可以做到這一切。下面讓我們來看看它的實現代碼:
class CookiesController < ApplicationController def create_cookie cookies[:the_time] = Time.now.to_s redirect_to :action =>"action_two" end def get_cookie cookie_value = cookies[:the_time] render(:text =>" #{cookie_value}") end end
在以上代碼中,在控制器中有兩個動作方法,一個是設置cookie的,另一個是讀取和顯示cookie值的。在這裡cookies[]是一個cookies對象數組,我們不需要聲明它,只需要將它看成一個普通數組即可。
接下來使用redirect_to方法通過參數:action將請新進行重定向。在get_cookie動作中,cookies的值被取出來,然後使用render()方法顯示這些值。
上面討論cookie的使用方法。但如果有一種方法可以透明使用cookie,那不是更好嗎?這個技術就是session。就象cookie一樣,session數組也無需聲明。它的用法類似於cookie對象。下面的代碼描述了session的使用。
class SessionController < ApplicationController def login user = User.find_by_name_and_password(params[:user], params[:password]) if user session[:user_id] = user.id redirect_to :action =>"index" else reset_session flash[:note] ="有戶名或密碼不正確!" end end
上面代碼對user_id和password進行核對。如果用戶存在,將這個用戶的user-id保存在session中。其中session[:user_id] = user.id的形式和保存cookie的形式完全一樣。接下來重定向到index頁上。如果用戶不存在,使用reset_session將session設為無效,並通過RoR返回簡單的提示信息。
3. 過濾和驗證
在一此情況下,在請求被處理之前,要進行一系列處理。這個過程就叫做過濾。過濾器所包含代碼需要在許多動作執行前或執行後被調用。因此,過濾器分為兩種,before過濾器和after過濾器。Before過濾器的代碼在請求被處理前被執行,而after過濾器恰恰相反,是在請求被處理之後執行過濾代碼。例如,驗證用戶身份代碼必須要在調用一個動作之前被調用,代碼如下示:
def authorize unless session[:user_id] flash[:notice] ="請登錄" redirect_to(:controller =>"login", :action =>"login") end end class AdminController < ApplicationController before_filter :authorize … …
以上代碼在AdminController中的任何動作被執行之前調用,而在控制器中要想調用authorize函數,必須在其中加上before_filter。after_filter的使用方法和before_filter類似。
過濾器雖然可以執行驗證代碼,但有時對請求需要更進一步的驗證,如此一來,過濾器就顯得捉襟見肘了。為了完成這些功能,我們就需要使用更為強大的驗證機制。在控制器中,可以通過verify實現更強大的驗證功能。如下面的代碼驗證了用戶提交方式。即用戶只能用post進行提交。
class BlogController < ApplicationController verify :only => :post_comment, :session => :user_id, :add_flash => { :note =>"You must log in to comment"}, :redirect_to => :index … …
4. 緩沖
從以上代碼可看出,服務器總是一遍一遍調用同樣的動作,如果調用這些動作很費時間的話,將會嚴重影響服務器的性能。因此,RoR為了解決這一問題,為我們提供了緩沖的功能。如果某一個動作經常被調用,將這個動作進行緩沖將是一個好主意。
在RoR中,可以通過caches_page來實現緩沖功能。緩沖可分為不同的層次,如對整個網頁進行緩沖,對動作緩沖,或是同時對網頁和動作進行緩沖。如在一個blog管理應用程序,將大家經常訪問的內容進行緩沖的代碼如下:
class ContentController < ApplicationController before_filter :verify_premium_user, :except => :public_content caches_page :public_content … …