2011年8月4日 星期四

[web_service]自己寫一個 webservice server 及 client

因為要開發測試 web service 的應用程式,
最痛苦的地方就是 client 要連到已存在的 server,
而該 server 不能亂搞,而又不是自己寫的,沒有模擬器。
在一種最尷尬的情況就是,
你的程式就是 web service,還需要使用別人的 web service,
在沒有模擬器的狀況下,開發及除錯都很難,
一遇到要 call out 就跳過,是最保險,但又是最麻煩的做法。
有辦法自己寫一個 web service 的 client 及 server 嗎?
當然有,這裡介紹兩種方法:

第一種是用微軟的 Web Service 模組,這種是最偷懶的方法。
但這種方法一不小心就是會找不到問題在哪,
而我就是因為粗心而花了兩天找問題。
第二種是用任何語言的 http listener / client 模組,
這種方法是最直覺,最能夠把問題分離化的方法。
可以控制到 http 層當然控制度最好。
這一篇是因為開發測試的時候因為粗心大意,結果浪費時間的心得報告。

-----------------------------------------

使用微軟的Web Service 模組當「模擬 server」的時候,
可以只模擬部份的 web method,
如果 client 端有下列方法,

string LoadProduct( string xml)

「模擬 server」就要宣告一個方法如下:

[Web Method]
string LoadProduct( string xml) { ….. }

要非常注意兩件事,

一、WebServcie 的 namespace,要跟 client 端的 namespace「一模一樣」,
少一個,多一個字元都會導致解不開 soap 物件,而且不會有任何錯誤訊息出現。
我這次就是最尾端少一個斜線,http://tempurl.orghttp://tempurl.org/ 的差別。
找了兩天,做了很多測試,甚至自己用 http listener 來回應測試。

二、web method 的參數名字,也要跟 client 端的參數名字「一模一樣」,
名字不一樣,參數值一樣解不開。

我出錯在這兩個地方,只要這兩個地方沒有錯,雖然不保證一定成功,
但至少錯不在這!

使用微軟的Web Service 模組當「模擬 client」的時候,非常簡單,
這時候一定會有 server 來的 wsdl,就當正式的 client 來寫程式就行了。

-------------------------------------------------

用 http listener 當「模擬 server」的時候,要處理到 http 的回應,
若是不知道 soap 規格,也不知道 wsdl 怎麼看,又該如何處置呢?

抄!這是這次解決方法的最高秘技。
在微軟的2008開發環境中,把 web service 啟動之後,
可以連到類似 http://localhost:1234/abcd.asmx
點擊任何一個 web method 就會看見測試框。

image

也有範例,這就是我們要抄的地方。

image

如果要當 client,就抄第一段。如果是要當 server,就抄第二段。
如果你要模擬的 server 是用微軟工具開發的,網址尾端是 asmx 的,
就會有這個網頁,就有範例可以抄。

用 http listener 當「模擬 server」,以 .Net 為例,
System.Net.HttpListener 可以拿到對方送來的 context,
request 裡有對方送來的資料,需要的話在 request 裡找。
response 的話,從範例來看,要設定

response.ContentType = "text/xml; charset=utf-8";

把範例抄進來,弄一個變數 responseString 來存放。
為了要把它弄成 utf8,需要

byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);

response 要設定長度

response.ContentLength64 = buffer.Length;

最後把 byte 寫下去,還要記得關起來,就行了。

System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();

這就完成一次傳輸。
最簡單可重覆就是用一個無限迴圈,不要用的時候直接把程式關了。
有問題就關掉重開。這是最簡單的方式。

---------------------------------------------------------------

用 http client 當「模擬 client」,以 .Net 為例,
System.Net.WebClient 物件產生之後,
參考範例,要在 headers 設定 Content-Type,及 SOAPAction。
內容的部份用一個 string 變數 soapcontent,存起來。
然後用 UploadString 把 soapcontent 送出去,該 method 回傳 server 的回應。

比較完整的程式碼如下,有些可能在貼上來的時候,雙引號被弄掉了,
我有看到的我都補上了,剪下貼上的時候要看一下。
完整的 server 的 main 如下:

static void Main(string[] args)
        {
            System.Net.HttpListener hl = new System.Net.HttpListener();
            hl.Prefixes.Add("http://*:1234/");
            hl.Start();
            while (true)
            {
                System.Net.HttpListenerContext context = hl.GetContext();
                System.Net.HttpListenerRequest request = context.Request;
                System.IO.StreamReader sr = new System.IO.StreamReader(request.InputStream);
                Console.WriteLine("read:");
                Console.WriteLine(sr.ReadToEnd());
                System.Net.HttpListenerResponse response = context.Response;
                response.ContentType = "text/xml; charset=utf-8";
                string responseString = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><LoadProductResponse xmlns=\”http://tempurl.org/\”><LoadProductResult>string</LoadProductResult></LoadProductResponse></soap:Body></soap:Envelope>";
                Console.WriteLine("write:");
                Console.WriteLine(responseString);

                byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
                response.ContentLength64 = buffer.Length;
                System.IO.Stream output = response.OutputStream;
                output.Write(buffer, 0, buffer.Length);
                // You must close the output stream.
                output.Close();
            }
            hl.Stop();
        }

完整的 client 的 main 程式碼如下:

static void Main(string[] args)
{
                System.Net.WebClient wc = new System.Net.WebClient();
                string soapcontent = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">  <soap:Body>    <LoadProduct xmlns=\"http://mirlmes.itri.org.tw\">      <InXml>string</InXml>    </LoadProduct>  </soap:Body></soap:Envelope>";
                wc.Headers["SOAPAction"] = "\"http://tempurl.org/LoadProduct\"";
                wc.Headers["Content-Type"] = "text/xml; charset=utf-8";
                Console.WriteLine(wc.UploadString(“http://localhost:1234/”, soapcontent));
}

沒有留言:

張貼留言