r29 - 04 Mar 2011 - 14:12:49 - AveMaria?You are here: SETEC Wiki >  Knowledge Web  > WebTechnologyCategory > RubyOnRails > WebServiceOnRails
-- CharinyaKlakhang - 03 Aug 2006

Web Services on Rails

การสร้าง web sevice client

ถ้ามองลงมาในระดับสูง การ implement web service สามารถแบ่งออกเป็น 2 กลุ่มได้แก่

  • server
  • client

web service ส่วนมากจะอยู่บน 1 ใน 3 ของสถาปัตยกรรม ได้แก่

  • Representational State Transfer (REST),
  • Simple Object Access Protocol (SOAP), หรือ
  • Extensible Markup Language Remote Procedural Calls (XML-RPC)

web services จะ access ผ่านทางสถาปัตยกรรม 2 อันหรือมากกว่าได้ เช่น ทั้ง SOAP และ XML-RPC เข้าถึง clients ใน ActionWebService? server ซึ่ง client จะสามารถ implement สถาปัตยกรรมได้ และจะพูดในหัวข้อถัดไป

ถ้าต้องการสร้าง web service clients ใน ruby และ implement ผ่าน rails application เค้าบอกว่า "You are in luck" การสร้าง web service client จะมีขั้นตอนง่ายๆ ที่จะไปเกียวข้องกับบาง libraries ของ ruby ถึงแม้ว่าจะใช้ libralies หลักๆ เพื่อการสร้างได้แก่ clientsCGI , NET , REXML , SOAP4R , XSD, และ XML-RPCare เหล่านี้จะถูกโหลดมาใช้งานอย่างอัตโนมัติ โดย Rails environment โดยทั้งหมดไม่ต้องกังวลว่าจะนำ libralies เหล่านี้ ไปใช้ที่ไหน เมื่อไหร่ อย่างไร ซึ่งจะอธิบายต่อไป

web service ส่วนมาก ไม่ใช่ desktop program หรือ command-line program แต่ web service client ส่วนมากก็คือ servers : ที่มีหน้าที่รวมข้อมูลของ web service อื่นๆ แล้วมาทำการ repackage เพื่อทำตามวัตถุประสงค์หรือความต้องการ

โดยจะกล่าวถึง 3 web service ที่นิยมได้แก่ REST , SOAP, XML-RPC ซึ่งฟรีและ เห็นประโยชน์จากงานเหล่านี้ เช่น Yahoo! Search, Google Search, และ Flickr ในส่วนแรก จะพูดถึงการสร้าง client ด้วย REST,SOAP,XML-RPC


การ search Yahoo โดยใช้ REST

การใช้ REST architecture เป็นสิ่งที่ง่ายที่สุด ซึ่งส่วนมากแล้ว REST จะทำงานคล้ายๆกับมาตรฐานของ web page request ไม่มีอะไรแปลก โดย REST Application จะสร้างง่าย โดยจะ query ทาง URL เหมือนกันกับ web page request โดยปกติ และ HTTP server จะให้ผลลัพธ์จากการถูก request ออกมาในรูปแบบของ document ซึ่ง document ที่ได้มานี้จะอยู่ในรูปแบบของ XML (แม้ว่า XML ไม่ได้มี requirement แต่ service ก็จะให้ผลลัพธืมาในรูปแบบของ data structure ) ซึ่งในความเป็นจริง ถ้า web browser ของคุณสามารถแสดงผล XML ได้ คุณเพียงใส่ URL ที่ถูกสร้างจาก client แรกสุด ที่ URL bar และก็จะเห็น raw XML เป็นผลลัพธ์ ที่ถูกสร้าง(generate) มาจาก Yahoo

NOTE : The world wide web itself can be considered a REST system

REST Client ต้องการสิ่งเหล่านี้บนพื้นฐานของ rails

  1. ติดต่อกับ web service บนมาตรฐานของ GET และ POST request (ขึ้นอยู่กับ ความต้องการของ service) , การใช้ NET library
  2. เก็นผลลัพธ์ ใน memory REXML document
  3. Parse ผลลัพธ์ด้วย REXML library สำหรับ Rails application

ก่อนอื่นเราจะสร้าง controller อย่างง่ายที่จะทำงานเพื่อ แสดงผลการค้นหา 3 อันดับแรกใน Yahoo! ,การใช้ interface REST ของ Yahoo โดย Yahoo ต้องทำ web service API สำหรับการบริการหลายหย่าง ซึ่งรวมถึงการให้บริการค้นหาจาก Yahoo! search engine

โดย Yahoo search service API ปัจจุบันฟรี มีการกำหนดจำนวนผู้ request (5,000 request ต่อ 1 IP address ต่อวัน) การใช้ Yahoo API สำหรับทดสอบ code ด้านล่าง คุณจะต้องมี Yahoo! Developer's key ที่จะได้โดยตรงจาก web : http://api.search.yahoo.com/webservices/register_application

เมื่อคุณมี Yahoo! Developer's key คุณก็เริ่มสร้าง controller (จัดเก็บใน code_controller.rb ในโฟลเดอร์ app/controllers )

NOTE: ขณะนี้เรากำลังเขียน web service client เพื่อไปเรียก ผลการค้นหา 3 อันดับแรก จาก web service server (web Yahoo จะมี web service เพื่อรองรับบริการนี้อยู่แล้ว)

class CodeController < ApplicationController
  def yahootest
    query = CGI.escape("SEARCH TEXT")               # URL-encoded search value
    yahookey = "YOUR YAHOO DEVELOPER KEYs           # Your Yahoo! dev key
    url = "http://api.search.yahoo.com/" +          # The URL to the Yahoo!
      "WebSearchService/V1/webSearch?" +            # Search service
      "appid=#{yahookey}&query=#{query}" +
        "&results=3&start=1"
    result = Net::HTTP.get(URI(url))                # make the actual HTTP request
    @doc = REXML::Document.new result               # turn the results into a
                                                    # REXML document
 end
end

จากนั้นเราจะแสดงผลการค้นหา โดยการสร้าง view ชื่อ yahootest.rhtml ในโฟลเดอร์ /app/views/code :

<% @doc.root.each_element do |res| %>
 <b>Title:</b> <%= res[0].text.to_s %><br>
 <b>Summary:</b> <%= res[1].text.to_s %><br>
 <b>Link:</b> <a href="<%= res[2].text.to_s %>"><%= res[2].text.to_s %></a>
 <br><br>
<% end %>

หลังจากนี้ จะเสร็จแล้วสำหรับการสร้าง web service client โดย Rails application ด้วย code ที่สั้นมาก โดยคุณสามารถทดสอบ application ของคุณทาง Webbrick : ที่ http://localhost:3000/code/yahootest
yahooREST.png

ตอนนี้เราจะได้การทำงานในฝั่ง client ซึ่งการทำงานของ code นี้ ขั้นแรก แม้ว่าไม่มีการ require statements ไลบรารีืั้สำคัญที่ client ใช้ก็จะมี NET, CGI , REXML ทั้ง 3 ตัวนี้จะอยู่ใน ruby 1.8.4 เป็นมาตรฐานอยู่แล้ว และจะถูกโหลดนำมาใช้โดย Rails application โดยคุณไม่ต้องทำอะไรพิเศษนอกจากรู้ว่า code นี้ทำอะไร

บางคนอาจเคยได้ยินไลบรารี NET และ CGI มาก่อนแล้ว ที่ Rails จะนำมาใช้เพื่อให้ทำให้ web-friendly แต่นั่นไม่ใช่จากไลบรารี REXML ซึ่งเป็น pure Ruby XMK Processor มีคุณสมบัติที่ดีมากมาย รวมถึงการสนับสนุน XPath 1.0 คุณสามารถเข้าไปอ่านรายละเอียดของ REXML เอกสารเต็มนี้ ได้ที่นี่ http://www.germanesoftware.com/software/rexml/

เราใช้ไลบรารี CGI สำหรับ escape our search term และต้องแน่ใจว่า search term นี้ใช้ HTTP GET request

 
query = CGI.escape("SEARCH TEXT")

ต่อไปเราจะเก็บ Yahoo! Developer's key ในตัวแปรและสร้าง URL ด้วยพารามิเตอร์ที่ระบุในเอกสารสำหรับ Yahoo! การเพิ่มพารามิเตอรื และ options สามารถเข้าไปดูเอกสารได้ที่นี่ค่ะ http://developer.yahoo.net/

 
yahookey = "YOUR YAHOO DEVELOPER KEY"

url = "http://api.search.yahoo.com/WebSearchService/" \
 "V1/webSearch?appid=#{yahookey}&query=#{query}&" \
 "results=3&start=1"

NOTE: คุณสามารถเห็นเอกสาร XML ที่จะได้จาก Yahoo! โดยเพียงแค่คุณใส่ URL บน web browser ที่สามารถ render XML ได้


การเข้าใจความหมาย หรือที่มีของ URL ไม่ได้ยาก จะอธิบายดังนี้ เราเรียก version1(V1) ของ Web serch service โดยส่งค่าพารามิเตอร์จำนวน 4 ตัว คือ
  • appid (the developer key)
  • query (search string)
  • results (ในที่นี้เราต้องการ 3 จำนวน)
  • start (จะบอกให้ web service ของ Yahoo! ว่าให้แสดงชุดของ results จำนวนที่เท่าไหร่ กำหนดได้ตามที่เราต้องการ)

ถ้าเป็นขนาดใหญ่ เราอาจจะต้องให้พารามิเตอร์เหล่านี้ อยู่ในรูปแบบของ set of results ซึ่งจาก code ด้านบนเราต้องการให้ผลลัพธ์ในครั้งแรก แสดงออกมา 3 จำนวน (ใช้ start = 1) และในขั้นต่อไป เราก็จะใช้ไลบรารี NET ที่จะทำ HTTP GET Request และจัดเก็บเอกสารที่ได้มาในตัวแปร

 
result = Net::HTTP.get(URI(url))

ซึ่งเราจะได้เอกสารที่ถูกเก็บอยู่ในตัวแปร local เราใช้ไลบรารี REXML มาแปลงผลลัพธ์ ไปเป็นเอกสาร REXML

 
@doc = REXML::Document.new result

ขั้นตอนสุดท้าย เราจะใช้ ไลบรารี REXML มา parse เอกสาร XML และแสดงผล โดยใช้ตัวเลขเป็นทางในการเข้าถึง tag และ attribute ในลักษณะคล้าย array wow professions wow gold reviews wow engineering guide wow first aid guide wow enchanting guide wow cooking guide wow blacksmithing guide wow inscription guide wow alchemy guide wow leatherworking guide wow jewelcrafting guide wow tailoring guide wow farming spots buying wow gold safe wow gold fast make money in wow professions best wow addons

 
<% @doc.root.each_element do |res| %>
 <b>Title:</b> <%= res[0].text.to_s %><br>
 <b>Summary:</b> <%= res[1].text.to_s %><br>
 <b>Link:</b> <a href="<%= res[2].text.to_s %>"><%= res[2].text.to_s %></a>
 <br><br>
<% end %>

เราใช้เมธอด each_element ดึงข้อมูลในเอกสารตั้งแต่ element root ออกมาแสดง (ResultSet?) ออกมาแสดง ดังนั้น element จึงเป็น subclass ของ parent ซึ่งเราจะมองเป็นลักษณะของ array ทีี่จะไปดึงข้อมูลตัวลูกออกมาแสดง ได้แก่ Title, summary, url ซึ่งถ้าไปอ่านเอกสารการใช้วาน REXML ก็จะรู้ว่าการเข้าถึง attribute และ value สามารถทำได้อย่างไร นักพัฒนาส่วนใหญ่จะรู้สึกว่า การสร้าง web service client โดยใช้ REST จะมีการ implement ที่ง่ายกว่าแบบอื่น และขณะเดียวกัน ถ้าเปรียบเทียบในภาษา ruby แล้ว การสร้าง web service ด้วย REST จะเข้าในการทำงานยากกว่าการสร้าง web service client ที่ SOAP หรือ XML-RPC นั่นเป็นเพราะว่า 2 ตัวนี้ที่ RUBY จะมีการจัดการรายละเอียดของงานในระดับ low-level โดยผู้พัฒนาไม่ต้องจัดการ เช่น communication กับ server ,การสร้าง XML Request หรือกรณีอื่นๆ แม้แต่การ parse XML response ทั้งหมดนี้จะเกิดขึั้นอัตโนมัติ

ต่อไปเราจะมาเรียนรู้การเขียน SOAP client ชนิด ด้วย Rails ว่าง่่ายอย่างไร


การ search Google โดยใช้ SOAP หรือ SOAP with WSDL

เมื่อไม่กี่ปีมานี้ SOAP เป็นมาตรฐานที่ได้รับการสนับสนุนมาก เนื่องจากมีเอกสาร (officials document) ออกโดยองค์กร W3C (World Wide Web Consortium)

SOAP มีปัญหาในเรื่องของความซับซ้อน แต่ Rails ได้ซ่อนมันเอาไว้ การสร้าง SOAP client มี 4 ขั้นตอนดังนี้

  1. สร้าง instance ของ SOAP driver
  2. กำหนดเมธอด SOAP ที่คุณต้องการจะเรียก
  3. เรียกเมธอด SOAP
  4. ใช้ผลลัพธ์ใน Rails Application

ซึ่งเราจะสร้าง Google search โดยใช้ SOAP ซึ่งเป็น web service API ฟรี สรำหรับการให้บริการที่หลากหลาย รวมไปถึง search engine ด้วย การใช้ Google search API นี้่(จากตัวอย่าง code) คุณจะต้องมี free Google Developer's Key โดยไปที่นี่ https://www.google.com/accounts/NewAccount?continue=http://api.google.com/createkey&followup=http://api.google.com/createkey

  • หลังจากเดือนธันวาคมปี 2006 Google ไม่ดำเนินการออก Google Developer's Key สำหรับ Search API แล้ว ให้ไปใช้ AJAX API แทน

และต่อไปด้านล่างนี้ จะเป็น controller ที่ใช้ SOAP ค้นหาผลลัพธ์ 3 อันดับแรกของการค้นหา โดยเขียน code ชื่อไฟล์ code_controoler.rb ซึ่งจากตัวอย่างนี้จะมีเมธอด googletest อยู่ ดังนี้ Google Key = 'ecDc6bFQFHLXPKTEO0McWIoE3LVRSEw2'

class CodeController < ApplicationController
  def googletest
     yourkey = 'YOUR GOOGLE DEVELOPER KEY'        # Your Google dev key
     @yourquery = 'SEARCH TEXT'                           # Search value
     XSD::Charset.encoding = 'UTF8'                       # Set encoding for response
     googleurl = "http://api.google.com/search/beta2"
     urn = "urn:GoogleSearch"
     driver = SOAP::RPC::Driver.new(googleurl, urn)  # Create our driver
     driver.add_method('doGoogleSearch', 'key', 'q',   # Set up methods we'll call
        'start', 'maxResults', 'filter', 'restrict','safeSearch', 'lr', 'ie', 'oe')
     @result = driver.doGoogleSearch(yourkey,         # make our SOAP request
        @yourquery, 0, 3, false, '', false, '', '', '')
  end
end

และการเขียน view จัดเก็บในไฟล์ googletest.rhtml ที่ app/views/code/

 Query for: <%= @yourquery %><br>
 Found: <%= @result.estimatedTotalResultsCount %><br>
 Query took about <%= @result.searchTime %> seconds<br>
<% @result.resultElements.each do |rec| %>
   <b>Title:</b> <%= rec["title"] %><br>
   <b>Summary:</b> <%= rec.snippet %><br>
   <b>Link:</b> <a href="<%= rec["URL"] %>"><%= rec["URL"] %></a>
   <br><br>
<% end %>

เสร็จเรียบร้อยแล้ว ที่นี้คุณลองทดสอบการสร้าง SOAP ว่าสมบูรณ์หรือยัง โดยพิมพ์ที่ browser : http://localhost:3000/code/googletest.

a.png

client จะใช้ library ruby 2 ตัว คือ SOAP4R และ XSD4R ซึ่ง SOAP4R เป็นภาษา ruby 100% มา implement SOAP 1.1 standard สำหรับเอกสารและข้อมูลเพิ่มเติม สามารถอ่านได้ที่นี่ http://dev.ctor.org/soap4r ส่วน XSD4R library ได้รับการสนับสนุนจาก library XML ที่ถูกใช้โดย SOAP4R สามารถอ่านเอกสารฉบับเต็มเกียวกับ library XSD ได้ที่นี่ http://rubydoc.org/stdlib/libdoc/xsd/rdoc/index.html โดย libraries SOAP4R และ XSD4R จะเป็นส่วนหนึ่งที่อยู่ใน ruby 1.8.4 และจะถูกโหลดมาใช้อัตโนมัติ โดยคุณไม่ต้องทำการ "require"

อธิบายการสร้าง instance ของ SOAP driver โดยใช้ Google SOAP service และ namespace

googleurl = "http://api.google.com/search/beta2"
urn = "urn:GoogleSearch"
driver = SOAP::RPC::Driver.new(googleurl, urn)  

ถัดไปจะอธิบายเมธอด ที่จะถูกเรียกต่อ โดย Google SOAP service และถูกแสดงอยู่ในเอกสารของ Google API online

driver.add_method('doGoogleSearch', 'key', 'q', 'start', 'maxResults', 'filter',
'restrict', 'safeSearch', 'lr', 'ie', 'oe')

ตามเอกสารจะบอกว่า Google web service จะให้ผลลัพธ์เป็น UTF-8 encoded ซึ่งมีบางอักขระพิเศษ ที่ ruby ไม่สามารถจัดการกับ string นั้นได้ ทำให้ driver เกิด throw XSD::ValueSpaceError จึงต้องตั้งค่าอักขระของเราให้เป็น UTF-8

XSD::Charset.encoding = 'UTF8' 

ซึ่งขณะนี้ผลลัพธ์จะถูก encode ให้กับสตริงใน ruby แล้ว ส่วน library Soap4r และ WSDL factory จะกล่าวถึงภายหลังว่าต้องอาศัย library XSD เพื่อที่จะแปลงชนิดข้อมูล ที่มาจากผลลัพธ์ของ web service ไปเป็นพื้นฐานของภาษา ruby

ในการทำงานของ controller มีการเรียกเมธอด doGoogleSearch ซึ่งเป็นเมธอดของ driver ที่เราได้สร้างและ config ขึ้นมาก่อนแล้ว

@result = driver.doGoogleSearch(yourkey, @yourquery, 0, 3,
                                 false, '', false, '', '', '')

การแสดงผล ที่ได้จาก SOAP Mapping objects นั้น มันจะถูกเข้าถึงผ่าน results เช่น @result.estimatedTotalResultsCount หรือ hash values @result["estimatedTotalResultsCount"] เพราะว่า ruby ระบุไว้ว่าตัวแปรที่ขึ้นต้นด้วยตัวใหญ่จะเป็นชื่อ class หรือชื่อค่าคงที่ การเรียกเมธอด ตัวอักษรตัวแรกของชื่อเมธอดจะถูกแปลงเป็นตัวเล็กโดยอัตโนมัติ ซึ่งกำลังจะบอกว่าในภาษา Ruby ไม่จำเป็นที่ชื่อเมธอดจะต้อง match กับเมธอดใน web service แต่บางครั้งอาจจะรู้สึกว่าไม่เหมาะสม เช่น คุณจะใช้ค่าจากตัวแปรว่า URL ซึ่งจะได้ผลลัพธ์อยู่ในresult.uRL ซึ่งดูแปลกจากความเป็นจริง ดังนั้นจึงมีการเข้าถึงตัวแปรอีกแบบเรียกว่า "hash" สำหรับเมธอดที่ต้องการให้ตัวอักษรตัวแรกเป็นพิมพ์ใหญ่ เช่น rec["URL"] ในกรณีนี้การเขียน code โดยใช้ hash จะดูดีมากกว่า ซึ่งใน view ของเราจะใช้รูปแบบการเข้าถึงเพื่อแสดง result ทั้งสองแบบเลย

    Query for: <%= @yourquery %><br>
    Found: <%= @result.estimatedTotalResultsCount %><br>
    Query took about <%= @result.searchTime %> seconds<br>
    <% @result.resultElements.each do |rec| %>
     <b>Title:</b> <%= rec["title"] %><br>
     <b>Summary:</b> <%= rec.snippet %><br>
     <b>Link:</b> <a href="<%= rec["URL"] %>"><%= rec["URL"] %></a>
     <br><br>
<% end %>

ตัวอย่างนี้ดูปกติ แต่ก็มีจุดด้อยเล็กน้อย หลังจากสร้าง SOAP driver เราต้องเรียก add_method เพื่อประกาศเมธอดที่เราจะเรียกใช้ ซึ่งการทำแบบนี้ ไม่มีความยืดหยุ่น ยากต่อการขยาย และเกิด bug ได้ เนื่องจากคุณจะต้องจำว่าถ้ามีการเพิ่ม feature ที่จะเรียกเมธอดใหม่ของ API คุณต้องมาแก้ไขการเรียก add_method เราจะแก้ปัญหาโดยการใช้ไฟล์ WSDL ที่ซึ่งเป็นรายละเอียด XML ของ web service's API และ soap/wsdlfactory library

WSDLDriverFactory? จะดาวน์โหลดไฟล์ WSDL ทำการประมวลผล และสร้าง SOAP driver ที่เข้าใจกับ service's API อย่างไรก็ตามก็จะมีข้อเสียอย่างนึง คือเมื่อ Rails start จะไม่มีการโหลด soap/wsdlfactory อัตโนมัติ ดังนั้นเราต้องแน่ใจว่า เราได้ config environment ซึ่งต่อไปจะอธิบายขั้นตอนการสร้าง SOAP client สำหรับ Rails application โดยใช้ไฟล์ WSDL

  1. Update ไฟล์ environment.rb เพื่อโหลดไลบรารี WSDLDriver
  2. สร้าง SOAP instance จากไฟล์ WSDL
  3. เรียกเมธอดของ service
  4. ใช้ results ใน Rails application ของคุณ

จากตัวอย่าง Google ที่จะใช้ไฟล์ WSDL แทนการกำหนดเมธอดที่เราจะใช้ เริ่มที่การแก้ไขไฟล์ environment.rb

require 'soap/wsdlDriver'
เมื่อเรา start Webrick server มันก็จะไปเปลี่ยน environment และคุณก็จะสามารถใช้ WSDL ที่ controller
class CodeController < ApplicationController
  def googletest
     yourkey = 'YOUR GOOGLE DEVELOPER KEY'                # Your Google dev key
     @yourquery = 'SEARCH TEXT'                           # Search value
     XSD::Charset.encoding = 'UTF8'                       # Set encoding
     wsdlfile = "http://api.google.com/GoogleSearch.wsdl"     # WSDL location
     driver = SOAP::WSDLDriverFactory.new(wsdlfile).create_rpc_driver     # Create driver
                                                                          # and set up
                                                                          # methods
     @result = driver.doGoogleSearch(yourkey, @yourquery, # make our SOAP request
                 0, 3, false, '', false, '', '', '')
     end
end

การเปลี่ยนแปลงที่เห็นได้ชัดก็คือการสร้าง driver โดยใช้ไฟล์ WSDL ที่ถูกจัดหามาจาก Google web service นั่นก็คือการระบุไปที่ URI และ namespace

 driver = SOAP::WSDLDriverFactory.new(wsdlfile).create_rpc_driver

เราได้ยกเลิกการเรียก driver ไปที่ add_method เพราะว่าข้อมูลได้มาจากไฟล์ WSDL ซึ่งเราจำเป็นต้องรู้ ในเรื่องของการ required parameter และค่าที่ return จากเมธอดที่เราเรียก

ไม่มีอะไรเปลี่ยนแปลงไปจากการแสดงผล และคุณสามารถทดสอบได้ที่ http://localhost:3000/code/googletest เอกสารที่สมบูรณ์เกี่ยวกับคุณลักษณะของไฟล์ WSDL สามารถดูได้ที่ http://www.google.com/apis/

เรามี web service client อีกชนิดนึงคือ XML-RPC ที่จะถูกสร้างเป็น client ของ Flickr service ที่ได้รับความนิยม


การแสดงรูปภาพจาก Flickr โดยการใช้ XML-RPC

Flickr is primarily a photo-sharing service provided by Yahoo!, though it has recently branched out to offer other features. Basic accounts are free, and Flickr has a web service API that provides access via REST, SOAP, and XML-RPC. Because we've already covered SOAP and REST, we'll build our client using the XML-RPC architecture.

Flickr เป็น photo-sharing service ตัวแรกๆที่ provided โดย Yahoo! แม้ว่าปัจจุบันจะถูกแตกออกเป็นรูปแบบอื่นออกไป ซึ่งพื้นฐานจะฟรี และ Flickr มี web service API ที่กำหนดให้เข้าถึงได้ทาง REST , SOAP และ XML-RPC แต่เราเคยกล่าวถึง SOAP และ REST แล้ว เราจึง จะสร้าง client นี้ โดยใช้ XML-RPC architecture

เราต้องมี Frickr key สำหรับการเข้าถึง API ของมัน เนื่องจาก Frickr เป็นส่วนหนึ่งของ Yahoo! เราต้องตั้งค่า Yahoo! acount ก่อนเป็นอันดับแรก เพื่อ จะใช้ Frickr service โดยไปที่ http://www.flickr.com/signup เพื่อสมัคร หลังจากนั้นก็จะได้ key เตรียมพร้อมที่จะสร้าง XML-RPC client

FLICKR_API_KEY = "f588cb6945cc27a2c593a4a21f7e5641"

ขั้นตอนการจัดการ Rails application เพื่อสร้าง XML-RPC client

  1. สร้าง RPC driver
  2. เรียกใช้เมธอด
  3. ใช้ results ใน Rails application ของคุณ

เราต้องการให้แสดง 3 รูปภาพล่าสุดจาก Frickr โดยการใช้เมธอด interestingness.getList แก้ไขไฟล์ code_controller.rb จากตัวอย่างจะสร้างเมธอด flickrtest ใน controller ดังนี้

class CodeController < ApplicationController
  def flickrtest
     flickruri = "http://www.flickr.com/services/xmlrpc/"
     server = XMLRPC::Client.new2(flickruri)
     flickrkey = "YOUR FLICKR KEY"
     details = {:api_key => flickrkey, :per_page => "3"}
     result = server.call("flickr.interestingness.getList", details)
     @doc = REXML::Document.new result
   end
end

And here's the view that displays the images and their titles. Save the following code as flickrtest.rhtml in the app/views/code/ directory):

และเขียนการแสดงรูปภาพและ title ในไฟล์ flickrtest.rhtml ที่ path app/views/code/ ดังนี้

<%
@doc.root.each_element do |res|
  image_url = "http://static.flickr.com/" \
   "#{res.attributes["server"]}/" \
   "#{res.attributes["id"]}_#{res.attributes["secret"]}.jpg"
%>
   <%= image_tag(image_url, :border => "0", :height => "100") %><br>
   <%= res.attributes["title"] %>
   <br><br>
<% end %>

เราจะรันไปที่ http://localhost:3000/code/flickrtest. จะเห็นการแสดงผลดังภาพ
pixXMLRPC.png

frickrtest ใช้ไลบรารีพิเศษของ Ruby คือ XMLRPC ซึ่งถูกเขียนด้วยภาษา ruby (pure ruby) และมีอยู่ใน Ruby 1.8.4 เป็นมาตรฐาน XMLRPC จะถูกโหลดอัตโนมัติและเตรียมไว้สำหรับ Rails application สำหรับเอกสาร และข้อมูลเพิ่มเติม ตลอดจนรายงาน bug ดูได้ที่ http://www.ntecs.de/projects/xmlrpc4r/

การสร้าง XML-RPC จะเรียกใช้ปกติและตรงไปตรงมา เรื่มที่การสร้าง instance ของ driver

flickruri = "http://www.flickr.com/services/xmlrpc/"
server = XMLRPC::Client.new2(flickruri)

และสร้างเมธอดไว้เรียก

details = {:api_key => flickrkey, :per_page => "3"}
result = server.call("flickr.interestingness.getList", details)

เราจะตั้งค่า Hash เพื่อส่งค่า Frickr key และกำหนดจำนวน result นั่นก็คือ 3 (เป็นการกำหนดโครงสร้างของ data type ของเอกสาร) XMLRPC library คล้ายกับ SOAP4R library คือมีการ map Ruby Hash กับโครงสร้างชนิดข้อมูลของ web service เมื่อมีการสร้าง request ออกไป เราไม่ต้องเพิ่มอะไรเลย สำหรับการ mapping ระหว่าง web service และ native ruby ไม่ว่าจะเป็น outgoing หรือ incoming requests.

NOTE : โครงสร้างของ Web service จะมีการอ้างถึง complex data types และ map ไปที่ Ruby Hash type

สุดท้าย ในตัวอย่างของ REST เราจะแปลง result ไปสู่เอกสาร REXML ที่เราสามารถ parse

@doc = REXML::Document.new result

เราจำเป็นต้องแปลง results สู่เอกสาร REXML โดยเมธอด interestingness.getList จะ return results เป็น simple string และจัดเก็บในเอกสาร XML ผลลัพธ์จาก XML-RPC ที่ซับซ้อนนี้ จะถูก access คล้ายกับ SOAP results ในตัวอย่างก่อนหน้านี้ ไม่ว่าจะด้วยเมธอดหรือ hash

เมื่อเรานำ results เป็นเอกสาร REXML และ parse แล้ว ใน Frickr's online document จะบอกว่า เราจะสร้าง URL อย่างไรเพื่อแสดงรูปภาพจาก results ที่ได้ และแสดง title ในแต่ละรูปภาพออกมา

<%
@doc.root.each_element do |res|
  image_url = "http://static.flickr.com/" \
   "#{res.attributes["server"]}/" \
   "#{res.attributes["id"]}_#{res.attributes["secret"]}.jpg"
%>
   <%= image_tag(image_url, :border => "0", :height => "100") %><br>
   <%= res.attributes["title"] %>
   <br><br>
<% end %> 


การสร้าง web sevice server

ชนิดของ web service 3 ชนิดที่ได้รับความนิยม ได้แก่ REST,XML-RPC , SOAP

การสร้าง Web Service ด้วย REST

ถ้าต้องการสร้าง web service ด้วย Rails Application นั้น จะมีการใช้ RXML template ที่จะเป็นพื้นฐานการสร้าง REST server ด้วย custom XML document ซึ่งใน code ของคุณต้องเป็นไปตามนี้
  1. ต้องปิด layouts ของเมธอดที่จะใช้เป็น service
  2. สร้า้งการเชื่อมโยงระหว่าง RXML template กับเมธอดที่เป็น service ของคุณ(ใน controller) แทน RHTML template
เมื่อพร้อมแล้วก็เข้าสู่การสร้างดังนี้

เขียนที่ controller ในไฟล์ app/controllers/code_server.rb และสร้างเมธอดชื่อ restserver
class CodeServerController < ApplicationController
    layout "application", :except => [:restserver]
    def restserver
         @sampledata = ["Kevin","Timothy","Catherine"]
    end
end
note: จะเห็นว่ามีการตั้งค่าให้ยกเว้นการแสดง layout ให้กับเมธอด restserver

ส่วนการแสดงผล เขียนที่ view ในไฟล์ app/views/code_server/restserver.rxml
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
xml.exampledata do
    counter = 0
    @sampledata.each do |rec|
       counter = counter + 1
       xml.name(rec, :id => counter)
    end
end

note: จะเห็นว่าไฟล์ชื่อ restserver.rxml ไม่ใช่ restserver.rhtml เหมือนเดิมแล้ว เนื่องจากไฟล์นี้ต้องการให้แสดงผลเป็น xml document จากนั้น ทดสอบการใช้งา่นได้ที่นี่ http://localhost:3000/code_server/restserver จะได้ผลดังนี้

restserver.png

จากตัวอย่างนี้จะใช้ library ของ ruby ชื่อ Builder library ตามเอกสารที่ได้ศึกษานี้ Builder library จะเตรียมโครงสร้างข้อมูลเพื่อสร้างเอกสาร XML โดยปกติแล้วมันจะถูกติดตั้งมาพร้อมกับ Rails อยู่แล้ว แต่เพื่อความแน่ใจคุณสามารถติดตั้ง version ล่าสุดได้ที่คำสั่ง gem :

gem  install  builder
เอกสารสมบูรณ์ของ builder library อยู่ที่นี่ http://builder.rubyforge.org

จาก code ข้างต้นนั้น มันง่ายเกินไป แต่ในความเป็นจริงแล้ว controller จะต้องเพิ่ม action อื่นๆอีกที่เป็นการเชื่อมกับ web ทั่วไป นั่นก็จะหมายความว่าจะต้องใช้ RHTML template ที่เป็นมาตรฐาน ดังนั้นเราจำเป็นต้องปิดการแสดงผล layout เฉพาะ actions ที่เป็นส่วนหนึ่งของ REST service (restserver action) โดยใช้พารามิเตอร์ except :

layout "application", :except => [:restserver] 
note: ถ้าในโปรแกรมของคุณมี 2 template ทั้ง restserver.rhtml และ restserver.rxml มันจะให้ความสำคัญกับ RHTML ก่อน แสดงว่ามันจะไปเอาข้อมูลการแสดงผลที่ไฟล์ restserver.rhtml ดังนั้นคุณจะต้องระวังกับนามสกุลของไฟล์ เมื่อมีการสร้าง REST-based service

ใน code ข้างต้นนี้ มีเพียงเมธอด restserver ที่จะเตรียม pack ตัวแปร instance กับข้อมูลที่มีการแสดงผล XML document ในส่วนของ view ซึ่ง view (ในที่นี้จะพูดถึง RXML Template) จะใช้ Builder library ในการสร้างเอกสาร XML ซึ่ง ActionPack? library ใน rails รู้ว่าจะนำ Builder library มาใช้เมื่อไหร่ ถ้าพบ view ที่เป็น RXML template

การสร้าง XML โดยใช้ Builder ก็จะตรงไปตรงมา เริ่มจากการเรียกเมธอด xml.instruct ที่ xml เป็น object ที่พบใน RXML template :

xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"

การใส่ tag ในเอกสาร xml นี้จะมาจากการเรียกเมธอดของ xml object ซึ่งมีความหมายดังนี้ ตัวอย่าง

xml.name("Kevin", :id => 1, "age" => "31")
   # creates <name id="1" age="31">Kevin</name>

   * ชื่อเมธอด(name) => คือ ชื่อ tag
   * พารามิเตอร์ตัวแรก ("kelvin") => คือเนื้อหาของ tag นี้
   * Hash value (:id => 1, "age" => "31") => คือ attribute ของ tag

และ

xml.exampledata do
     xml.name("Kevin")
 end
 # creates <exampledata><name>Kevin</name></exampledata>


การสร้าง Web Service ด้วย SOAP และ XML-RPC

Rails จะมั component ที่เรียกว่า ActionWebService? (AWS) ที่จะมาช่วยในการสร้าง web service server ให้ง่ายและมีประสิทธิภาพ โดยตัว AWS นี้จะอนุญาติให้คุณนำเมธอดของ SOAP และ XML-RPC มาผูกกับ controller ที่จะมาสร้างเป็น web service ใน Rails application โดยมันจะเข้ามาจัดการในส่วนของ parsing XML request, การสร้าง XML response หรือแม้แต่การสร้างไฟล์ WSDL ใน SOAP service

ต้อง update เวอร์ชันของ ActionWebService? หรือติดตั้งแยกต่างหากจาก rails ก็ได้ โดยใช้คำสั่งนี้

   gem install actionwebservice

AWS สนับสนุนกับเครื่องมือของ rails ที่จะทำให้วงจรของการพัฒนาเร็วขึ้น ซึ่งได้มีการรวมเมธอดของ web_service_scaffold และ script เพื่อสร้างไฟล์, source code , และบางฟังก์ชั่น test ที่เป็นพื้นฐานให้ สร้างด้วยคำสั่งนี้

script/generate web_service YOURSERVICE YOURMETHOD1 YOURMETHOD2


พิมพ์คำสั่ง เพื่อสร้างไฟล์พื้นฐานในการสร้าง web service

0.png


โครงสร้างของ rails apllication เดิม

11.png


โครงสร้างของ rails apllication ใหม่

2.png


โครงสร้างของ rails apllication ใหม่ หลังจากพิมพ์ command line ด้านบนแล้ว

3.png

note: ทั้งหมดนี้เป็นการสร้าง web service ด้วย AWS และขั้นตอนในการสร้าง service มี 3 ข้อที่คุณต้องรู้

  1. ตัดสินใจว่าจะใช้ dispatching mode ไหนที่เหมาะสม ในการสร้าง web service ของคุณ (Direct, Delegated, Layered)
  2. สร้าง Application Programming Interface(API) เตรียมรายละเอียดเกี่ยวกับเมธอด ที่คุณต้องการ
  3. สร้างเมธอด (ไม่ว่าจะเป็น controller สำหรับการทำแบบ direct mode หรือ model สำหรับการทำแบบ delegated,layered)
เริ่มการสร้าง web service ด้วย single method ชื่อ dogreeting โดยเมธอดนี้จะทำงานโดยการมองช้ามค่าของตัวแปร :username แต่จะ return ค่าให้ :greeting ซึ่งเป็นตัวแปร string แทน เราจะเขียน code ไฟล์ API ที่นี่ app/apis/greeting_api.rb

class GreetingApi < ActionWebService::API::Base
   api_method :dogreeting,
     :expects => [{:username => :string}],
     :returns => [{:greeting => :string}]
end

note: คุณต้องใช้ command line ในการสร้าง (ruby script/generate webservice greeting dogreeting) base file และมาปรับแก้ code ในภายหลัง

เขียน controller ที่จะ implement เมธอด ที่เคยกำหนดในไฟล์ API ใน rails จะหา GreetingApi? ที่จะถูก implement ใน GreetingController? ดังนั้นเราจะสร้างไฟล์ไว้ที่นี่ app/controllers/greeting_controller.rb

class GreetingController < ApplicationController
  wsdl_service_name 'Greeting'

  def dogreeting
        "Hello" #{username}
  end
end


ดังภาพ

4.png

หลังจากนั้น เราสามารถสร้าง web service server แบบง่ายๆได้ ด้วยวิธี direct-dispatching mode โดย ActionWebService? จะสร้างไฟล์ WSDL สำหรับ service นี้อัตโนมัติ , SOAP client สามารถดาวน์โหลดไฟล์ WSDL จาก http://127.0.0.1:3000/greeting/wsdl ได้เลย


สร้างไฟล์ WSDL ให้อัตโนมัติดังนี้

5.png

เราสามารถตรวจสอบว่า service นี้สามารถทำงานได้ โดยเขียน code ทดสอบไว้ที่นี่ app/test/functional/greeting_api_test.rb ดังนี้

require File.dirname(__FILE__) + '/../test_helper'
require 'greeting_controller'

class GreetingController; def rescue_action(e) raise e end; end

class GreetingControllerApiTest < Test::Unit::TestCase
  def setup
    @controller = GreetingController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
  end

  def test_dogreeting
    #result = invoke :dogreeting
    #assert_equal nil, result
    result = invoke :dogreeting, "Charinya"
    assert_equal "Hello Charinya", result
  end
end


*ดังภาพ*

test.png

คุณต้องมี Webrick ที่กำลัง run อยู่ 1 command window และจะทำการทดสอบอีก window นึงแยกต่างหาก คำสั่งที่ใช้ทดสอบคือ

ruby test\functional\greeting_api_test.rb
ถ้าสำเร็จจะได้ผลดังนี้

Loaded suite greeting_api_test
Started
.
Finished in 0.75 seconds.
1 tests, 1 assertions, 0 failures, 0 errors.



test2.png



test3.png

จากตัวอย่างช้างต้นจะเห็นความได้เปรียบของการใช้ rails เช่น การตั้งค่าของ dispatching mode และ การติดต่อสัมพันธ์กันระหว่าง API และ Controller ซึ่งจะอธิบายดังนี้ ขันแรกในการสร้าง web service ด้วย Rails นั้นต้องมีการออกแบบ และมากำหนด dispatching mode ที่เหมาะสม ซึ่งใน AWS จะมี 3 mode ได้แก่ :direct, :delegated, :layered โดย dispatching mode จะเป็นตัวควบคุมเส้นทาง การเรียกเมธอดของ web service จาก client ถึงเมธอดใน Ruby ซึ่งทั้ง 3 ตัวนี้จะมีการ implement เมธอดแตกต่างกัน และ ฝั้ง client ก็ะใช้เป็นตัว access web service ของคุณ ซึ่งการระบุ mode เหล่านี้จะทำที่ controller ให้ service เรียกไปที่เมธอด web_service_dispatching_mode ด้วย argument :direct, :delegated, :layered

Mode default คือ :direct (web_service_dispatching_mode :direct) ซึ่งแบบนี้จะมี 1 controller , 1 API โดยคุณจะเขียนโปรแกรมเหมือนกับเขียน Application บน rails ตามปกติ ยกเว้นเมธอด web service จะไม่มี template RHTML views ข้อเสียของ :direct mode คือ จะต้องเชื่อม 1 API ต่อ 1 controller เท่านั้น ซึ่งข้อจำกัดนี้กลายมาเป็นปัญหา เช่นถ้าคุณมีหลายจุดที่ให้เข้าถึง web service หรือต้องการรวม service หลายๆอันที่มีอยู่ เป็น 1 web service ขนาดใหญ่

เช่น ถ้าพัฒนาแบบ :direct mode ที่ web service จะให้บริการ user ค้นหาข้อมูลที่ yourdomain.com/data/getproductdata และ yourdomain.com/data/getuserdata แต่อีกกรณีนึงจะเกิดปัญหา ถ้าต้องการจะแยกกันเป็น 2 access point แบบนี้ yourdomain.com/user/getdata และ yourdomain.com/product/getdata ใน :direct mode ต้องมี 2 controller (สำหรับ product และ user) นั่นก็หมายความว่าเราต้องเขียน code ใน controller ทั้ง 2 ไฟล์มีพื้นฐานเหมือนกัน ซึ่งผิด concept ของ Rails คือ DRY (don't repeat your self)

สำหรับ 2 mode ที่เหลือ คือ :delegated, :layered การเขียนโปรแกรม 2 แบบนี้จะเหมือนกัน คือจะกำหนดเมธอด web service ที่ model และเชื่อมความสัมพันธ์ระหว่าง API กับ model โดยจะอ้างถึง model จาก controller ได้ตามต้องการ ดังนั้นเราจะแก้ปัญหา user และ product ข้างต้นได้ โดยสร้าง model 2 แยกกัน ได้แก่ user model และ product model แต่ละ model ก็จะมีเมธอด getdata เป็นของตัวเอง ซึ่งจะสัมพันธ์กับไฟล์ API 1 ไฟล์ ที่จำกำหนดไว้ให้ relate getdata webservice input output parameter สุดท้ายในแต่ละ controller จะใช้เมธอด webservice ประกาศว่าเมธอดไหนที่สามารถเรียกใช้งานได้จากข้างนอกเช่น

 web_service :getdata,Product.new 

ถ้าไม่พูดถึง dispatching mode จะพัฒนา web service บน rails โดยไปประกาศเมธอดที่มีทั้งหมดและชนิดข้อมูลที่ web service ต้องการ ไว้ที่ API และ AWS ใช้ API นี้ไปสร้างเส้นทางทีถูก request และสร้าง error ถ้าขาดพารามิเตอร์หรือconnectionมีปัญหา ส่วนเมธอด api_method จะเป็นการประกาศเมธอดที่ีจะเป็น service ส่วน option ที่มีคือพารามิเตอร์ :expect และ :return เพื่อสัญลักษณ์บอกว่าเป็น web service ถ้าคุณตัด :expect ออกไป web service จะสร้าง error ให้กับ client ใดๆก็ตามที่พยายามส่งพารามิเตอร์ระหว่างเรียกเมธอดนี้ และถ้าเอา :return ออกไปทุกๆ client ที่เรียกเมธอดนี้จะไม่ได้รับค่าอะไรเลย

Ruby เป็นภาษาที่ไม่เข้มงวด :expects และ :returns อาจจะรู้สึกแปลก แต่จำเป็นสำหรับการสร้าง web service ซึ่งการระบุชนิดของข้่อมูล (data type) ให้พารามิเตอร์และการ return value จะเหมาะสมมากกับ web service ที่จะไปติดต่อกับ client ต่างๆที่เขียนด้วยหลากหลายภาษา รวมถึงภาษาที่ statically เช่น java, C# การที่เราไม่กำหนดชนิด เป็นไปได้ที่สามารถส่งให้ client ได้ โดยมี object ที่ไมได้่คาดหวังและไม่มีการดักจับข้อมูล เป็นเหตุให้ client fail หรือ การส่งข้อมูลขัดแย้งกัน ผลลัพธ์ผิดพลาด

เมธอด api_method option :expects และ :returns ใช้ hash key ที่พารามิเตอร์เป็นชนิด array และ return value ก็เป็นชนิด array (คล้ายเมธอดใน ruby เมธอดใน webservice จะสนับสนุน multiple return value ) โดย element ของ array เหล่านี้เป็นชนิดข้อมูลมาตรฐานในภาษา ruby (:string, :int, :bool, :float, :time, :datetime, :date, :base64) หรือชื่อของ ActiveRecord::Base หรือ classes ActionWebService::Struct

การเขียน web service server ด้วย dispatching mode :delegated หรือ layered อ่านต่อที่นี่


ลิงค์ภายนอก ที่น่าสนใจ

  • Ruby Network Programming แนะนำความรู้พื้นฐานพร้อมตัวอย่างของ network programming รวมถึงการตัวอย่างการพัฒนาเว็บเซอร์วิสโดยใช้ REST, XML-RPC และ SOAP
  • REST of Rails ตัวอย่างของการพัฒนา REST Web Services ด้วย Ruby on Rails

-- ChumpholKrootkaew - 11 Apr 2006 - 15:13

toggleopenShow attachmentstogglecloseHide attachments
Topic attachments
I Attachment Action Size Date Who Comment
pngpng a.png manage 237.3 K 21 Jul 2006 - 16:34 CharinyaKlakhang  
pngpng pixXMLRPC.png manage 187.9 K 31 Jul 2006 - 09:06 CharinyaKlakhang  
pngpng yahooREST.png manage 227.5 K 31 Jul 2006 - 09:08 CharinyaKlakhang  
pngpng restserver.png manage 34.9 K 04 Aug 2006 - 14:14 CharinyaKlakhang  
pngpng 0.png manage 16.7 K 08 Aug 2006 - 12:08 CharinyaKlakhang  
pngpng 1.png manage 33.8 K 08 Aug 2006 - 12:08 CharinyaKlakhang  
pngpng 3.png manage 22.8 K 08 Aug 2006 - 12:08 CharinyaKlakhang  
pngpng 4.png manage 23.9 K 08 Aug 2006 - 12:08 CharinyaKlakhang  
pngpng 5.png manage 35.1 K 08 Aug 2006 - 12:09 CharinyaKlakhang  
pngpng 11.png manage 19.3 K 08 Aug 2006 - 12:10 CharinyaKlakhang  
pngpng 2.png manage 23.6 K 08 Aug 2006 - 17:39 CharinyaKlakhang  
pngpng test.png manage 123.6 K 09 Aug 2006 - 09:21 CharinyaKlakhang  
pngpng test2.png manage 23.0 K 09 Aug 2006 - 09:25 CharinyaKlakhang  
pngpng test3.png manage 13.8 K 09 Aug 2006 - 09:27 CharinyaKlakhang  
Edit | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r29 < r28 < r27 < r26 < r25 | More topic actions
Knowledge.WebServiceOnRails moved from Knowledge.RORWebServices on 19 Jul 2006 - 20:52 by CharinyaKlakhang - put it back
 
Powered by SETEC Wiki
Copyright ©2012 by National Electronics and Computer Technology Center, NECTEC.
Ideas, requests, problems regarding SETEC Wiki? Send feedback