--
CharinyaKlakhang - 03 Aug 2006
Web Services on Rails
การสร้าง web sevice client
ถ้ามองลงมาในระดับสูง การ implement web service สามารถแบ่งออกเป็น 2 กลุ่มได้แก่
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
- ติดต่อกับ web service บนมาตรฐานของ GET และ POST request (ขึ้นอยู่กับ ความต้องการของ service) , การใช้ NET library
- เก็นผลลัพธ์ ใน memory REXML document
- 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
ตอนนี้เราจะได้การทำงานในฝั่ง 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 ขั้นตอนดังนี้
- สร้าง instance ของ SOAP driver
- กำหนดเมธอด SOAP ที่คุณต้องการจะเรียก
- เรียกเมธอด SOAP
- ใช้ผลลัพธ์ใน 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.
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
- Update ไฟล์ environment.rb เพื่อโหลดไลบรารี WSDLDriver
- สร้าง SOAP instance จากไฟล์ WSDL
- เรียกเมธอดของ service
- ใช้ 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
- สร้าง RPC driver
- เรียกใช้เมธอด
- ใช้ 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. จะเห็นการแสดงผลดังภาพ
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 ของคุณต้องเป็นไปตามนี้
- ต้องปิด layouts ของเมธอดที่จะใช้เป็น service
- สร้า้งการเชื่อมโยงระหว่าง 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

:
จะเห็นว่ามีการตั้งค่าให้ยกเว้นการแสดง 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

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

: ถ้าในโปรแกรมของคุณมี 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
โครงสร้างของ rails apllication เดิม
โครงสร้างของ rails apllication ใหม่
โครงสร้างของ rails apllication ใหม่ หลังจากพิมพ์ command line ด้านบนแล้ว

: ทั้งหมดนี้เป็นการสร้าง web service ด้วย AWS และขั้นตอนในการสร้าง service มี 3 ข้อที่คุณต้องรู้
- ตัดสินใจว่าจะใช้ dispatching mode ไหนที่เหมาะสม ในการสร้าง web service ของคุณ (Direct, Delegated, Layered)
- สร้าง Application Programming Interface(API) เตรียมรายละเอียดเกี่ยวกับเมธอด ที่คุณต้องการ
- สร้างเมธอด (ไม่ว่าจะเป็น 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

:
คุณต้องใช้ 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
ดังภาพ
หลังจากนั้น เราสามารถสร้าง web service server แบบง่ายๆได้ ด้วยวิธี direct-dispatching mode โดย
ActionWebService? จะสร้างไฟล์
WSDL สำหรับ service นี้อัตโนมัติ , SOAP client สามารถดาวน์โหลดไฟล์
WSDL จาก
http://127.0.0.1:3000/greeting/wsdl ได้เลย
สร้างไฟล์ WSDL ให้อัตโนมัติดังนี้
เราสามารถตรวจสอบว่า 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
*ดังภาพ*
คุณต้องมี 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.
จากตัวอย่างช้างต้นจะเห็นความได้เปรียบของการใช้ 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 อ่านต่อที่นี่
ลิงค์ภายนอก ที่น่าสนใจ