3。Ruby 和 SOAP
簡易物件存取通訊協定 (SOAP)(SOAP)很快的成為了遠端程序呼叫(RPC)的標準協議。(更多關於SOAP的資訊可以分別參看http://www.linuxmagazine.com/2001-10/soap_04.html 和 http://www.linuxmagazine.com/2002-08/webs_01.html)
Ruby提供了對SOAP的強大支援,不管在用戶端還是服務端來說都是這樣的,使用SOAP4R,只需要4部分就能建立一個SOAP請求:
- 一個端點 (endpoint), 或者處理SOAP請求的網路地址,一個endpoint一般來說都是運行在WEB伺服器環境中的代碼,但是也有一些其它的SOAP傳輸,包括郵件。
- 一個命名空間(namespace), 定義了一個環境內容,在這裡解析調用的方法名。
- 一個方法名稱。 遠端程序呼叫的方法的名字。
- 一組參數。
使用SOAP4R的時候,我們需要在建立SOAP驅動的時候指定前兩個參數,第三個參數則是為這個驅動Binder 方法時候使用,最後的參數是調用實際需要的方法的時候使用。
比如,我們有一個處理銷售訂單的SOAP運行在http://my.server.com,我們要在用戶端訪問這個服務,先要建立一個 SOAP::Driver 對象,建立這個對象的時候需要指定命名空間和伺服器位址(建立這個對象的方法的前兩個參數和記錄日誌有關,這裡我們可以不用考率)
NS = "urn:ordersService"SVR = "http://my.server.com/orders"drv = SOAP::Driver.new(nil, nil, NS, SVR)
一旦我們建立了這個驅動,就可以用它的addMethod方法增加我們需要向伺服器調用的方法的名稱,第一個參數是這個方法的名稱,其餘的參數是這個方法需要的參數名,這裡,我們需要訪問的方法名為orders_for_product(譯者註:此處是否應該是orders_for?),傳給它的參數十客戶帳號和產品代碼。(Ruby不用WSDL描述SOAP介面)
drv.addMethod("orders_for", "cust_acct","prod_code")
一旦所有的東西都完成,我們可以用這個方法任意次數的向伺服器調用:
customers.each do |cust_acct| products.each do |prod_code| orders = drv.orders_for(cust_acct, prod_code) process(orders) endend
這個例子也要得益於Ruby語言的動態性,比如orders_for方法動態加到了SOAP驅動對象,所以我們可以用drv.orders_for來調用這個方法。而且我們也不需要定義這個方法的傳回值的形式,SOAP驅動會自動將從伺服器得到的結果轉換為適當地Ruby對象。
為了更詳細的說明一下,我們來看一個真正的例子。清單4顯示了如何用SOAP從googel搜尋引擎取得查詢結果,這段代碼基於Ian Macdonald的google.rb。運行這個程式之前,你需要在google 上註冊一下 (http://www.google.com/accounts) ,然後取得一個key,在第三行指定你得到的key。
清單4: 使用SOAP查詢google 1 require "soap/driver" 2 ENDPOINT = 'http://api.google.com/search/beta2' 3 NS = 'urn:GoogleSearch' 4 KEY = "get_a_key_from_google" 5 6 fail "Missing query args" if ARGV.empty? 7 8 query = ARGV.join(" ") 910 soap = SOAP::Driver.new(nil, nil, NS, ENDPOINT)11 soap.addMethodWithSOAPAction(12 'doGoogleSearch', NS, 'key', 'q', 'start', 'maxResults', 13 'filter','restrict', 'safeSearch', 'lr', 'ie', 'oe')14 res = soap.doGoogleSearch(15 KEY, query, 0, 10, false, nil, false, nil, 'latin1', 'latin1')1617 puts "Estimated result count: " + res.estimatedTotalResultsCount1819 res.resultElements.each do |entry|20 puts21 puts "#{entry.URL}: #{entry.title}"22 puts entry.snippet23 end |
實際的google查詢是在google的伺服器上由方法doGoogleSearch執行的,這個方法接收10個參數(關於這10個參數的具體意義,可以參考google的web API文檔),但是我們的例子裡,我們只是指定了查詢條件,其它參數我們都使用了預設值。在第11行,我們給SOAP驅動增加了方法doGoogleSearch,第14行我們調用這個方法,執行真正的查詢。
從google返回的結果是很複雜的對象,從高層來看,這個結果包括這個查詢本身的一些資訊,比如開始和結束的索引值,查詢用到的條件,查詢消耗的時間等。查詢結果存在一個數組裡面。這和你手工在google頁面上查詢看到的編號的結果一樣。每條查詢結果記錄本身也是很複雜的對象,在我們的例子裡,我們只取出了它的標題,url,和一部分文本。
SOAP介面把我們的工作變得很簡單,它自動建立對結果對象的迭代,建立返回結果對象的各種屬性的存取方法,然後,我們可以簡單的使用:
res.resultElements.each do |element| ...end
如果你在命令提示字元下運行清單4的程式,查詢參數為"ruby soap",結果如下:
$ ruby google_search.rb ruby languageEstimated result count: 206000http://www.ruby-lang.org/en/: <b>Ruby</b> Home Page <b>...</b> Japanese Page If you can read this oriental <b>language</b>, <br> you can get more information about <b>Ruby</b>. Site <b>...</b>http://slashdot.org/developers/01/08/11/2211254.shtml: Slashdot | Progra.... <b>...</b> Programming in the <b>Ruby</b> <b>Language</b>. <b>...</b> This discussion has<br> been archived. No new comments can be posted.http://dev.rubycentral.com/faq/rubyfaq.html: The <b>Ruby Language</b> FAQ The <b>Ruby</b> <b>Language</b> FAQ. Originally by: Shugo Maeda.<br> Now maintained by Dave Thomas with help from Andy Hunt. <b>...</b> |
(註:此表為譯者折行之後的結果,原來的內容可能超出寬度,影響閱讀)
在Ruby中編寫一個SOAP伺服器端也是非常簡單的。你所需要做的就是需要發布的介面對象,然後把這些對象發布的SOAP伺服器的servlet上。你所編寫的對象不需要知道SOAP的任何什麼東西。比如,清單5表示的是一個簡單的類,有一個簡單的方法double,接收一個參數,返回兩個這個參數相加的結果。
清單5: The file doubler.rb, the Ruby SOAP doubling class class Doubler def double(arg) arg + arg endend |
要在SOAP伺服器中訪問這個方法,我們需要把它組裝到SOAP伺服器的命名空間上。在Ruby中,最簡單的方法是使用web伺服器工具箱WEBRick。結合soaplet.rb這個servlet代碼(在SOAP4R包的samples/webrick目錄下),我們可以用很少的一些代碼來實現一個完整的SOAP伺服器,代碼見清單6。
清單6: A Ruby SOAP server 1 require 'webrick' 2 require 'soaplet' 3 require 'doubler' 4 5 server = WEBrick::HTTPServer.new(:Port => 2001) 6 7 soaplet = SOAP::WEBrickSOAPlet.new 8 soaplet.addServant('urn:doublerService', Doubler.new) 9 server.mount("/doubler", soaplet)1011 trap("INT") { server.shutdown }12 server.start |
前三行只是簡單的引入了需要的庫,soaplet,Double類等。第5行是建立一個web server必須得步驟(本例連接埠為2001),第7行建立了一個soaplet(一個把SOAP請求發送到一個對象的servlet)。第8行把這個servlet幫定到一個Doubler對象,第9行將這個soaplet映射到web server的/doubler。第12行啟動了伺服器處理序序,但是第11行幹什麼用呢?當你啟動一個WEBRick服務時,這個伺服器程式將處理請求,返回結果,但是,我們想我們的伺服器程式能完整的關閉,第11行就是為了完成這種功能,這一行給server註冊了一個處理SIGINT訊號的處理器,當收到這樣的訊號時,調用server的shutdown方法,在大多數作業系統下,control-c將產生SIGINT訊號,所以,我們可以在命令提示字元下控制我們的網頁伺服器。
我們可以用下面清單7中的SOAP用戶端程式來測試一下伺服器端。第10行還展示了rescue的另一個特點:這個語句首先嘗試將參數(傳過來的時候為字串)轉換為整型,如果轉換失敗,則rescue將會捕捉這個異常,並且返回原來的參數。這樣的結果是double方法能接收的參數可以是整型的和字串型的,讓我們看看用戶端啟動並執行時候會有什麼不同。
$ ruby soap_client.rb 12 wiki24wikiwiki$
傳入參數為12,我們得到的結果為24,當我們傳入字串wiki的時候,我們得到的是wikiwiki。Ruby中類型的多態性也傳播到了SOAP介面。因為double方法是arg+arg,所以如果參數為整型,返回兩個數相加的結果,如果參數為字串,則返回兩個字串串連的結果。
清單7: The doubler SOAP client 1 require "soap/driver" 2 3 SVR = 'http://localhost:2001/doubler' 4 NS = 'urn:doublerService' 5 6 soap = SOAP::Driver.new(nil, nil, NS, SVR) 7 soap.addMethod('double', 'arg') 8 9 ARGV.each do |arg|10 arg = (Integer(arg) rescue arg)11 puts soap.double(arg)12 end |