XMLRPC – SOAP RPC – RESTful RPC
本文将以代码、Request和Response三个不同的视角来分析三种不同的RPC方式。
这是Leonard Richardson之《Restful Web Services》一书中的例子。
An example in Ruby:
#!/usr/bin/ruby -w # xmlrpc-upc.rb require 'xmlrpc/client' def find_product(upc) server = XMLRPC::Client.new2('http://www.upcdatabase.com/rpc') begin response = server.call('lookupUPC', upc) rescue XMLRPC::FaultException => e puts "Error: " puts e.faultCode puts e.faultString end end puts find_product("001441000055")['description'] # "Trader Joe's Thai Rice Noodles" |
Rewrite in Python:
#!/usr/bin/python import xmlrpclib def find_product(upc): server = xmlrpclib.ServerProxy('http://www.upcdatabase.com/rpc') result = server.lookupUPC(upc) if type(result) == dict: if result['found']: return result['description'] else: return result['message'] return "" print find_product("001441000055") # Trader Joe's Thai Rice Noodles |
Request:
<?xml version="1.0" ?> <methodCall> <methodName>lookupUPC</methodName> <params> <param><value><string>001441000055</string></value></param> </params> </methodCall> |
Response:
HTTP/1.1 200 OK Date: Mon, 16 Aug 2010 13:49:58 GMT Server: Apache Vary: Accept-Encoding Connection: close Content-Type: text/xml <?xml version="1.0"?> <methodResponse> <params> <param><value><struct> <member><name>upc</name><value><string>001441000055</string></value> </member> <member><name>pendingUpdates</name><value><int>0</int></value> </member> <member><name>isCoupon</name><value><boolean>0</boolean></value> </member> <member><name>ean</name><value><string>0001441000055</string></value> </member> <member><name>issuerCountryCode</name><value><string>us</string></value> </member> <member><name>found</name><value><boolean>1</boolean></value> </member> <member><name>description</name><value><string>Trader Joe's Thai Rice Noodles</string></value> </member> <member><name>size</name><value><string>12 oz.</string></value> </member> <member><name>message</name><value><string>Database entry found</string></value> </member> <member><name>issuerCountry</name><value><string>United States</string></value> </member> <member><name>lastModified</name><value><string>2006-10-01 18:11:27</string></value> </member> <member><name>upce</name><value><string>00144155</string></value> </member> </struct></value> </param> </params> </methodResponse> |
In actual, SOAP is not just about HTTP. It’s also about SMTP and some other protocols.
The reason why it’s based on HTTP is just for pass through firewall.
下面这个例子调用ChinaStock的SOAP API,获得沪深股市某只股票的行情。其实好多基金网站和股票行情网站都是这样干的。
// 61.147.124.120 // class SoapQuery { public static string GetSource() { return "http://www.webxml.com.cn/WebServices/ChinaStockWebService.asmx"; } // getStockInfo // public static string GetStockInfo(string code) { ChinaStockWebService.cn.com.webxml.www.ChinaStockWebService oService = new ChinaStockWebService.cn.com.webxml.www.ChinaStockWebService(); string[] info_byte = oService.getStockInfoByCode(code); string strInfo = ""; foreach (string strLine in info_byte) { strInfo = strInfo + "\n" + strLine; } return strInfo; } // Save Stock Image to local file // public static void GetStockImage(string code, string filename) { ChinaStockWebService.cn.com.webxml.www.ChinaStockWebService oService = new ChinaStockWebService.cn.com.webxml.www.ChinaStockWebService(); byte[] image_bytes = oService.getStockImageByteByCode(code); Image image = CovertUtil.byteArrayToImage(image_bytes); image.Save(filename); } }; |
运行结果
Request
// Request from CommView HTTP Analyzer POST /WebServices/ChinaStockWebService.asmx HTTP/1.1 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.3603) VsDebuggerCausalityData: uIDPowOEuUJZbqRJtxAFcA/WSHkAAAAA1gzbkv7ONEGsZ5se4/oaBJ00bq++H01LmgVGJKWdaA4ACQAA Content-Type: text/xml; charset=utf-8 SOAPAction: "http://WebXml.com.cn/getStockInfoByCode" Host: www.webxml.com.cn Content-Length: 354 Expect: 100-continue Connection: Keep-Alive HTTP/1.1 100 Continue xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><getStockInfoByCode xmlns="http://WebXml.com.cn/"><theStockCode>sh000001</theStockCode></getStockInfoByCode></soap:Body></soap:Envelope> |
Response
// Response from CommView HTTP Analyzer HTTP/1.1 200 OK Date: Mon, 16 Aug 2010 07:44:34 GMT Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50727 Cache-Control: private, max-age=0 Content-Type: text/xml; charset=utf-8 Content-Length: 832 <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><getStockInfoByCodeResponse xmlns="http://WebXml.com.cn/"><getStockInfoByCodeResult><string>sh000001</string><string>ä¸Šè¯æŒ‡æ•°</string><string>2010-08-16 15:03:09</string><string>2661.708</string><string>2606.700</string><string>2603.792</string><string>55.01</string><string>2596.771</string><string>2665.314</string><string>2.11%</string><string>1258628.23</string><string>13073685.1353</string><string /><string /><string /><string /><string /><string /><string /><string /><string /><string /><string /><string /><string /></getStockInfoByCodeResult></getStockInfoByCodeResponse></soap:Body></soap:Envelope> |
{code} // 60.28.0.266 // class RESTfulQuery { public static string GetSource() { return "http://hq.sinajs.cn"; } public static string GetStockInfo(string code) { string uri = "http://hq.sinajs.cn/list=" + code; HttpWebRequest request = CreateWebRequest(uri); //request.TransferEncoding = "GBK"; HttpWebResponse response = (HttpWebResponse)request.GetResponse(); StreamReader responsestream = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("GBK")); return responsestream.ReadToEnd(); } // Save Stock Image to local file public static void GetStockImage(string code, string filename) { string uri = "http://image.sinajs.cn/newchart/daily/n/" + code + ".gif"; try { HttpWebRequest request = CreateWebRequest(uri); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Image image = Image.FromStream(response.GetResponseStream()); image.Save(filename); } catch (Exception e) { MessageBox.Show("Failed to query Stock infromation"); filename = ""; } } public static HttpWebRequest CreateWebRequest(string uri) { return (HttpWebRequest)WebRequest.Create(uri); } }; |
运行结果
Request
// Request from Commview HTTP Analyzer GET /list=sh000001 HTTP/1.1 Host: hq.sinajs.cn |
Response
//Response from CommView HTTP Analyzer HTTP/1.1 200 OK Cache-Control: no-cache Content-Length: 164 Connection: Keep-Alive Content-Type: application/x-javascript; charset=GBK var hq_str_sh000001="ÉÏÖ¤Ö¸Êý,2603.792,2606.700,2661.708,2665.314,2596.771,0,0,125862823,130736851353,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2010-08-16,15:03:09"; |
RESTful方式的架构意味着方法信息体现在HTTP方法里;
XMLRPC and SOAP, all requests are based on POST method.
An interesting example of AWS is that Amazon called non-SOAP API as Query API, not RESTful API.
在Leonard的书中提到了ROA;它则表明作用域信息存在于URI内;
对于一个基于ROA的RESTful Web Service,我们应该从URI就能基本了解客户端的请求,而HTTP Request部分,则定义了方法的具体信息。
可能的Representation Format: XML, JSON, ATOM Publish, XHTML..