熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

跨越邊界: REST on Rails

2022-06-13   來源: Java核心技術 

  過去的 年間一個趨勢主導了商業軟件工具的開發用復雜性對抗復雜性這一趨勢在任何地方都沒有比在分布式計算領域更明顯C 和 Java&#; 社區已經看到一些驚人復雜的框架被構建出來支持分布式通信分布式計算環境(DCE)支持用 C 語言編寫的應用程序之間的遠程過程調用公共對象請求代理架構(CORBA)標准支持面向對象應用程序之間的通信企業 JavaBean(EJB)規范提供安全性持久性事務消息和遠程的服務對各個框架的宣傳甚囂塵上但是這些框架都沒有滿足預期有些甚至因為它們的復雜性而成為災難在這些框架中只有 EJB 屬於大力簡化的結果有潛力在分布式應用程序上成功市場可能給也可能不給這個面臨強敵的框架另一個空間但 EJB 仍然需要交付使用

  最新的大型分布式框架是 Web 服務Web 服務技術讓應用程序可以用平台獨立或編程語言獨立的方式相互通信Web 服務標准也受到復雜性惡魔的威脅但是稱作 REST 的替代策略承諾了更簡單的方式本文介紹了如何在 Ruby on Rails 中添加 REST 風格的 Web 服務並從 Ruby 和 Java 代碼調用服務


關於本系列

  在 跨越邊界 系列中作者 Bruce Tate 推動了這樣一個概念今天的 Java 程序員通過學習其他方法和語言可以受益編程陣營中 Java 技術是所有開發項目最佳選擇的情況已經變了其他框架正在改變 Java 框架構建的方式從其他語言學到的概念有助於 Java 編程編寫的 Python(或 RubySmalltalk 或 ……)代碼可以改變 Java 編碼的方式

  本系列介紹了與 Java 開發有根本不同但是卻直接適用的編程概念和技術在某些情況下需要集成這些技術以利用它在其他情況下將可以直接應用這些概念單獨的工具不如其他語言和框架中影響 Java 社區的開發人員框架甚至基本方法的思想重要

  Web 服務領域

  就像 EJBCORBA 和 DCE 一樣Web 服務的核心抽象也是遠程過程調用Web 服務利用叫做 SOAP(最初SOAP 代表簡單對象存取協議但是這個術語現在降級了)的協議用 XML 表示消息的結構這裡有一個技巧如果協議用代表簡單的 S 開始那它就不簡單Web 服務定義語言(WSDL)提供了服務的標准規范像 SOAP 一樣WSDL 也是一個棘手而復雜的 API而 SOAP 和 WSDL 僅僅涉及到了構成 Web 服務這個大怪物的眾多 API 的表面Web 服務需要一次大修感謝 Roy Fielding 的一份有影響的博士論文Web 服務得到了大修

  Fielding 的論文描述了 REST 應用程序聯網策略REST 與全堆棧 Web 服務根本不同主要原因有三個


REST 的核心抽象是遠程資源而不是遠程過程調用
REST 沒有發明一個詳盡的標准列表而是采用現有的 Internet 標准包括 HTTPXML 和 TCP/IP
REST 沒有覆蓋每個可能場景而是覆蓋了最常見的問題

  請把 REST 想像成浏覽REST 客戶使用與浏覽器相同的 HTTP 命令訪問資源當 REST 客戶訪問到資源的表示時客戶轉換到一個狀態使用不同的 HTTP 命令REST 客戶可以創建讀取更新或刪除資源的記錄

  例如以典型的博客為例通過輸入 URL例如 得到貼子的列表然後如果想編輯博客條目可以在 URL 中輸入 HTTP 參數(例如 /edit?article=然後顯示編輯表單由於每個博客條目都有自己的 URL所以點擊鏈接或直接輸入 URL就可以用 HTTP 命令讀取修改或刪除內容

  簡而言之REST 可以


用 TCP/IP 命名標准命名 Web 上的資源
用 HTTP 查詢和操縱這些資源
使用基於文本的標准消息格式(例如 XML 或 HTML)來構造數據

  Ruby on Rails 用 REST 對 Web 服務提供了優秀的支持

  Action Web Services 概述

  Rails 用叫做 Action Web Services 的模塊實現 Web 服務許多開發框架鼓勵視圖和 Web 服務使用獨立的控制器這個策略可以維護控制器之間的風格一致問題是針對所服務的每種內容都需要一個新控制器例如Ajax 用戶界面要求從控制器取得到 JavaScript 的遠程 XML 調用

  不必為 Web 服務專門分配一個控制器使用 Rails可以通用地用同一個控制器向基於 HTML 的視圖基於 XML 的 Web 服務和基於 XML 的 JavaScript 組件提供內容理解 Action Web Services 的最好方式就是在工作應用程序的環境下查看它的實際作用

  請用自己選擇的數據庫管理器創建一個叫做 service_development 的數據庫接下來用以下命令創建 Rails 項目和模型

  

  > rails service > script/generate model Person


  在生成模型之後就有了一個叫做 db/migrate/_create_peoplerb 的遷移請把這個遷移編輯成像清單 一樣

  清單 people 表的遷移

  class CreatePeople < ActiveRecord::Migration def selfup create_table :people do |t| lumn :first_name :string :limit => lumn :last_name :string :limit => lumn :email :string :limit => lumn :phone :string :limit => end end def selfdown drop_table :people end end


  把 config/databaseyml 中的數據庫配置修改成與自己的數據庫配置匹配並輸入 rake migrate最後輸入 script/generate scaffold Person PeoplePerson 模型和 People 控制器生成工作台現在可以用 script/server 啟動服務器了請把浏覽器指向 localhost:/people以看到針對 Person 的經典的 Rails 腳手架 顯示了帶有標准 Rails 腳手架的應用程序

   簡單的 Rails 應用程序
映射框架

  在我介紹 Rails 的 Web 服務之前請查看控制器代碼編輯 app/controllers/people_controllerrb使之與清單 的代碼匹配

  清單 PeopleController 的控制器代碼

  class PeopleController < ApplicationController def index list render :action => list end # GETs should be safe (see ) verify :method => :post :only => [ :destroy :create :update ] :redirect_to => { :action => :list } def list @person_pages @people = paginate :people :per_page => end def show @person = Personfind(params[:id]) end def new @person = Personnew end def create @person = Personnew(params[:person]) if @personsave flash[:notice] = Person was successfully created redirect_to :action => list else render :action => new end end def edit @person = Personfind(params[:id]) end def update @person = Personfind(params[:id]) if @personupdate_attributes(params[:person]) flash[:notice] = Person was successfully updated redirect_to :action => show :id => @person else render :action => edit end end def destroy Personfind(params[:id])destroy redirect_to :action => list end end


  如果跟著做過這個系列以前的 Ruby on Rails 項目就會知道典型的控制器方法的一般流程是


    用戶通過跟隨鏈接或指定 URL通過 HTTP 發送請求
    Web 服務器根據域的配置把請求轉給 Ruby on Rails
    Rails 路由器根據 URL 模式把請求路由給控制器默認模式是//主機名/控制器/動作/參數
    路由器用與動作相同的參數調用控制器上的方法
    動作參數為視圖設置實例變量並呈現視圖
    動作方法把實例變量拷貝到視圖

  例如請看 清單 中的 show 方法控制器設置視圖使用的 @person 實例變量因為方法沒有指定視圖的名稱所以 Rails 用與控制器動作相同的名稱調用視圖 —— 在這個示例中視圖位於 app/views/people/showrhtml

  再來看 list 方法如果想讓這個方法呈現 XML需要


刪除分頁
people 實例變量轉換成 XML
呈現 XML 而不是 HTML

  Rails 使得處理 Web 服務和呈現來自同一 Web 服務的視圖成為可能實際上也不需要分頁為了把 Web 服務的 list 方法簡化一些可以把控制器中的 list 方法變成像清單 一樣清除分頁還需要刪除靠近 app/views/people/listrhtml 代碼底部的 Next PagePrevious Page 鏈接

  清單 簡化 list

  def list @people = Personfind_all end


  由於刪除了分頁也就刪除了讓用戶界面更健壯的一個特性但是又得到了一些回報可以用相同的代碼來驅動 Web 服務和視圖如果日後發現需要分頁可以編寫一些定制的助手

  現在基本應用程序出來了可以添加一些 Web 服務了

  向 Rails 控制器添加 Web 服務

  如果我想說大話我可以說 現在已經有了一個 Web 服務記得我對 REST 說過什麼?這種風格的 Web 服務使用指定的資源我的 Rails 應用程序也具有指定的資源host_name/people/list 調用我的 list 服務REST 風格的 Web 服務也使用 TCP/IP 和 HTTP我的 Rails 應用程序就是這麼做的而且格式良好的 HTML 就是 XML 的子集也滿足最後一條 REST 要求只需在 localhost:/people/list 上調用 HTTP get並解析結果就可以得到人員列表這就是關鍵REST 的工作方式與 Internet 的工作方式一樣但這並不是真正基於 REST 的 Web 服務理想情況下應當提供反映 Person 含義的 XML 文檔而不是用戶界面的結構

  真正的服務應當產生純數據的表示一個專門針對服務的預期客戶而構建的表示但是示例應用程序有兩個客戶終端用戶和 REST 客戶要為兩個目的重用相同的代碼需要給 Rails 提供更多信息Rails 的設計者可能決定使用額外的 URL 參數但是處理 URL 可是一項費勁的工作Rails 不應當用這些細節增加用戶負擔相反HTTP 提供了指定更多信息的工具HTTP 頭

  要理解 Web 服務的 REST 模型了解一點 HTTP 是有幫助的curl(請把它想像成 查看 URL)命令允許用一個命令查詢 URL並查看響應基於 Unix 的操作系統默認包含 curl可以為其他操作系統下載免費的 curl 工具通過輸入 //someurl可以將請求限制成只輸出默認的響應體(浏覽器呈現的 HTML)輸入 curl //someurl 可以得到更多信息這個命令返回 HTTP 頭如清單 所示可以看到頭配置由表示每個請求的配置的鍵值對組成

  清單 用 curl 調用 HTTP 請求

  > curl i Alive Date: Tue Jun :: GMT ContentType: text/html; charset=UTF Server: WEBrick/ (Ruby//) ContentLength: SetCookie: _session_id=defbcdd; path=/


  後面將頻繁地看到 HTTP getputpostdelete 命令REST 利用達些命令執行經典的 CRUD(CRUD 是create readupdate 和 delete 的共同縮寫)HTTP 命令到 CRUD 的映射是這樣的


Create(創建)HTTP put
Read(讀取)HTTP get
Update(更新)HTTP post
Delete(刪除)HTTP delete

  浏覽器利用 HTTP 頭通過相同的服務器端代碼來滿足不同類型的請求行為良好的應用程序提供正確處理文檔的充足信息其中一條信息叫做 HTTP Accept只要多花一點力氣控制器就能利用一些助手Accept 頭決定如何響應進入的請求然後控制器可以呈現適當的響應請把 PeopleController 中的 list 方法改成像清單 一樣

  清單 擴展 list方法以呈現 XML

  def list # wants is determined by the http Accept header in the request @people = Personfind_all respond_to do |wants| l wantsxml { render :xml => @peopleto_xml } end end


  在清單 可以看到完整的基於 REST 的 Web 服務生成的代碼是 Rails 中小型的特定於域的語句的優美示例它擴展 Ruby 以構造一種 switch 語句它的工作方式是這樣的


    respond_to 方法接受單個代碼塊並傳遞一個實例變量(標為 wants)到代碼塊
    wants 對每個可能的類型都有一個方法控制器可以為控制器期望的每個類型指定一個代碼塊
    如果方法名稱與 HTTP Accept 頭中的類型匹配wants 方法執行對應的代碼塊
    如果沒有指定代碼塊(例如 lRails 就執行默認動作(在這個示例中呈現 app/views/people/listrhtml)

  這個策略允許在所有預期的客戶之間共享相同的設置代碼如果需要添加期望 HTML 的 JavaScript 客戶以便讓應用程序支持 Ajax只需要添加 wantsjs如清單 所示

  清單 為 JavaScript 客戶呈現 HTML

  def list # wants is determined by the http Accept header in the request @people = Personfind_all respond_to do |wants| l wantsjs wantsxml { render :xml => @peopleto_xml } end end


  現在已經看到了如何向只讀的方法中添加 REST Web 服務show 方法也類似如清單 所示

  清單 實現 show

  def show @person = Personfind(params[:id]) respond_to do |wants| l wantsxml { render :xml => @personto_xml } end end


  您可能已經注意到通過 REST 看到的只有只讀服務原因是讓應用程序處理提交和刪除所需要的工作比較少刪除不需要額外的支持因為當前的代碼已經用 URL 指定了要刪除的人的 IDRails 自動轉換 post 請求中進入的 XML所以不需要構建任何服務器端支持實際上應用程序不用變就能刪除更新和創建可以修補每個方法呈現的 HTTP 響應但是客戶代碼實際就在 HTTP 返回碼之後

  現在是調用 Web 服務的時候了

  調用 Web 服務

  使用現有 HTTP 協議這一策略使得調用變得簡單清單 顯示了 Ruby 版本請注意 HTTP Accept記住控制器根據這個頭決定內容的類型

  清單 從 Ruby 調用服務

  require net/http Net::HTTPstart(localhost ) do |http| response = httpget(/people/list Accept => text/xml) #Do something with the response puts Code: #{de} puts Message: #{ssage} puts Body:\n #{responsebody} end


  清單 中的 Web 服務調用在//localhost:/people/list 上調用 HTTP get 方法並輸出響應Ruby 有很好的庫可以處理生成的 XML但是它們超出了本文的范圍不需要用 Ruby 調用這個服務只需要 HTTP 的庫清單 顯示這個服務的 Java 調用

  清單 用 Java 代碼調用服務

  package comrapidredws; import *; import javaio*; public class SimpleGet { void get() { try { URL url = new URL(//localhost:/people/list); URLConnection urlConnection = urlopenConnection(); urlConnectionsetRequestProperty(accept text/xml); BufferedReader in = new BufferedReader(new InputStreamReader(urlConnectiongetInputStream())); String str; while ((str = inreadLine()) != null) { Systemoutprintln(str); } inclose(); } catch (Exception e) { Systemoutprintln(e); } }


  像其 Ruby 等價物一樣這個代碼打開一個 URL 連接Accept 頭設置成 text/xml發出 get並輸出結果Java 代碼有許多 XML 框架但是我在這個示例中硬編碼了 XML以保持示例簡單

  post 的調用也相似清單 顯示了簡單的 post

  清單 用 Java 代碼調用 HTTP post

  void post() { try { String xmlText = <person> + <firstname>Maggie</firstname> + <lastname>Maggie</lastname> + <email></email> + </person>; URL url = new URL(//localhost:/people/create); HttpURLConnection conn = (HttpURLConnection)urlopenConnection(); connsetDoOutput(true); connsetRequestMethod(POST); connsetRequestProperty(ContentType text/xml); OutputStreamWriter wr = new OutputStreamWriter(conngetOutputStream()); wrwrite(xmlText); wrflush(); BufferedReader rd = new BufferedReader(new InputStreamReader(conngetInputStream())); String line; while ((line = rdreadLine()) != null) { Systemoutprintln(line); } wrclose(); rdclose(); } catch (Exception e) { Systemoutprintln(Error + e); } }


  這個 HTTP post 通過在//localhost:/people/create 上調用 post並在 HTTP 文檔體中傳遞一個 XML 文檔創建了一個新 Person(通常應當用 Java XML 庫構建 XML 文檔這次我還是硬編碼了 XML 文檔以保持示例簡單)Rails 支持自動把進入的 XML 轉換成 Person 屬性的 Ruby 散列表

  結束語

  在本文中已經看到只用少量代碼就使控制器支持基於 REST 的 Web 服務動態類型化的 Internet 語句例如 Ruby大量地利用 REST 代替基於 SOAP 的 Web 服務一些簡單的調用包括漂亮的 responds_to 語法和對進入提交的自動 XML 轉換使得可以容易地利用同一控制器處理 Web 服務遠程 JavaScript 請求或 HTML

  Java 語言對 REST 也有非常好的支持畢竟servlet 實際上是服務器端基於 REST 的 Web 服務可以在 Java 端使用 servlet在 Ruby 端使用 Rails 控制器把利用兩個平台優勢的應用程序組合在一起這就是 Web 服務的漂亮之處您真正需要的所有東西就是超群出眾的勇氣


From:http://tw.wingwit.com/Article/program/Java/hx/201311/26105.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.