转自:http://web.nchu.edu.tw/~jlu/cyut/axis/axis1net.shtml#string,请看原作
開發 ASP.NET 的客戶端來存取 AXIS 的服務
開發 Web Services 的最主要的目的之一,就是希望能達到跨平台性, 然而可惜的是:事實並非如此,因為每一個廠商的 implementation 或多或少有一些差異。目前,在 Java 的世界,Axis 是最常用的 web service 平台(根據 Axis 的 FAQ,採用 Axis 技術的商用軟體包含 BEA's Weblogic Platform, Borland, IBM WebSphere, Oracle Web Services Manager, JBoss 等);而在微軟的世界裡, .NET 卻是最常見的 web services 平台;因此,這兩者之間的 interoperability (稱為互通性)就更形重要。在前一節的文章中,我已經介紹了如何開發 Axis 客戶端 程式來存取 .NET 的服務;在本文中,,我會敘述如何開發 ASP.NET 的客戶端程式 來存取 Axis 的服務 我參考的主要資料來源為:- Wiki's Notes on Axis and .Net Interop
- Web Services Using Axis
- Alex Ferrara, Matthew MacDonald, Programming .NET Web Services: Chapter 2, O'Reilly, 2002.
- Scott Mitchell, An Extensive Examination of Web Services: Part 7 -- Examines how the incoming and outgoing messages to a Web service can be programmatically modified via SOAP Extensions, ASP.NET 4Guys From Rolla.com. 好像有蠻完整的介紹。
- Microsoft, XML Web Services Created Using ASP.NET and XML Web Service Clients.
為了達到跨平台性,有幾個地方要特別注意的:
- 如之前說過的,傳送的 SOAP 資料格式必須設定成 wrapped/literal。這是因為 .NET 的預設傳送格式是 wrapped/literal,而 AXIS 卻不是。
- 目前確定可以交換的資料型態包含 Java 的 String、boolean、byte、short、int、long、float、以及 double。另外,上述資料型態的陣列,JavaBean,以及 JavaBean 的陣列也可以正確的傳送。
- 避免交換 multi-dimensional 和 jagged 陣列,以及避免利用 overloading 來 定義 web services。
我們的測試環境是:Windows XP Professional SP2 + IIS 5.1 + .NET Framework 2.0 + .NET Framework SDK 2.0。 開發的步驟包含:
- 利用 WSDL 檔案來產生 ASP.NET 的 proxy 檔案(這部份的概念跟 Axis 相同, ASP.NET 的程式也是利用 proxy 類別來跟遠端的服務來連結的,在本範例中, QueryTimeService.vb 就是 proxy 類別)。產生 proxy 類別的方式很簡單, 你只要使用 .NET Framework SDK 2.0 所提供的 wsdl.exe 即可;執行的指令為 wsdl /l:vb /protocol:SOAP http://localhost:8080/axis/services/TimeService?wsdl。你也可以不必使用 HTTP 存取 WSDL 檔,WSDL 檔也可以是一個 local 的檔案。由於我們開發的程式是以 VB 來撰寫,因此才有 /l:vb 的選項。
- 利用剛剛產生的原始碼來編譯並產生 .dll 檔(稱為 assembly)。 執行的指令為 vbc /out:bin\QueryTimeService.dll /t:library /r:System.Web.Services.dll /r:System.Xml.dll QueryTimeService.vb;QueryTimeService.vb 是前一個步驟所產生的原始碼,而這個步驟會產生一個 QueryTimeService.dll 的 .dll 檔。而這個 .dll 檔的儲存位置在目前這個路徑底下,名稱為 bin 的子目錄 內(由於執行 ASP.NET 的程式時,ASP 會自動從 bin 子目錄內去尋找所需的 .dll 檔,因此我們建議不要設定其它的輸出路徑)。假設我們執行第一和第二步驟 的路徑是 c:\Inetpub\wwwroot,則 QueryTimeService.vb 位於 c:\Inetpub\wwwroot,而 QueryTimeService.dll 位於c:\Inetpub\wwwroot\bin。
- 開發 ASP.NET 的客戶端程式。如果剛剛 QueryTimeService.dll 位於 c:\Inetpub\wwwroot\bin,則下列的 ASP.NET 的程式必須置放於c:\Inetpub\wwwroot。
<%@Page Language="VB" %> <script runat=server> Sub Page_Load(o As Object, e As EventArgs) ' 這個是 Axis 的 service Const URL as String ="http://hostname:8080/axis/services/TimeService" ' QueryTimeService 的類別已經由 wsdl.exe 和 vbc.exe 產生 ' 這個 QueryTimeService.dll 必須放在這個 .aspx 檔的同一個 ' 目錄底下的 bin 目錄;依照我們的範例,就是放在 ' c:\Inetpub\wwwroot\bin 內 Dim service as New QueryTimeService() ' 設定服務的 URL service.Url = URL ' 呼叫服務的 getTime() Dim Answer As String = service.getTime() Label1.Text = Answer End Sub </script> <html> <body> <h2 align="center">ASP SOAP Client</h2> <hr/> <font color="gray" size="5"> <asp:Label id="Label1" runat="server" /> </font> <p/><p/> </body> </html>
- 執行這個 ASP.NET 的程式會得到如下的畫面:
開發與使用回傳字串陣列的 AXIS 服務
由之前的討論,Axis 和 .NET 之間可以互傳字串陣列、符合 JavaBean 規格 的物件、以及物件陣列,我們就依序測試。首先測試的是字串陣列:- AXIS service: ArrayService.java 該服務會將服務的時間,以年、月、日、 時、分、秒的字串陣列回傳。請注意,由於 AXIS 的預設狀況 為 "Request" scope,也就是每一次 service 被呼叫的時候,AXIS 都會產生 一個新的 ArrayService 的物件,因此,每一次呼叫這個 service 都會回傳 最新的時間。
import java.util.*; public class ArrayService { private String[] date; public ArrayService() { Calendar now = Calendar.getInstance(); date = new String[6]; date[0] = String.valueOf(now.get(Calendar.YEAR)); date[1] = String.valueOf(now.get(Calendar.MONTH) + 1); date[2] = String.valueOf(now.get(Calendar.DATE)); date[3] = String.valueOf(now.get(Calendar.HOUR)); date[4] = String.valueOf(now.get(Calendar.MINUTE)); date[5] = String.valueOf(now.get(Calendar.SECOND)); } public String[] getTimeArray() { return date; } }
- 註冊服務,該服務的 WSDD 檔 deploy.wsdd 如下所示:
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="ArrayService" provider="java:RPC" style="wrapped" use="literal"> <parameter name="className" value="ArrayService" /> <parameter name="allowedMethods" value="*" /> </service> </deployment>
- 將 service 佈置好了以後,就可以開始開發客戶端程式。
- 開發 AXIS 的客戶端程式
- 我們利用 AXIS 所提供的 WSDL2Java 的工具來產生所需要的 proxy 類別,然後開發客戶端程式如下:
- ArrayClient.java
import java.rmi.*; import javax.xml.rpc.*; import _aa._bb._cc._cc.axis.services.ArrayService.*; public class ArrayClient { public static void main(String[] args) throws RemoteException, ServiceException { ArrayServiceService service = new ArrayServiceServiceLocator(); ArrayService call = service.getArrayService(); String[] result = call.getTimeArray(); System.out.println(result[0]); System.out.println(result[1]); System.out.println(result[2]); } }
- 開發 .NET 的客戶端程式
- 將 service 佈置好了以後,就可以開始開發客戶端程式。過程跟之前所說的 一樣,先利用 wsdl.exe 以及 service 的 WSDL 檔來產生 proxy 類別,然後 在依照 proxy 類別產生 .dll 檔。
- VBArrayClient.aspx
<%@Page Language="VB" %> <script runat="server"> Sub Page_Load(o As Object, e As EventArgs) ' 這個是 Axis 的 service Const URL as String ="http://hostname:8080/axis/services/StringArray" Dim service as New ArrayServiceService() ' 設定服務的 URL service.Url = URL ' 呼叫服務的 getTime() Dim Answer() As String = service.getTimeArray() Label1.Text = Answer(0) Label2.Text = Answer(1) Label3.Text = Answer(2) Label4.Text = Answer(3) Label5.Text = Answer(4) Label6.Text = Answer(5) End Sub </script> <HTML> <BODY> <h2 align="center">ASP SOAP Client</h2> <hr/> <font color="gray" size="5"> 年/月/日: <asp:Label id="Label1" runat="server" />/<asp:Label id="Label2" runat="server" />/<asp:Label id="Label3" runat="server" /><br/> 時/分/秒: <asp:Label id="Label4" runat="server" />/<asp:Label id="Label5" runat="server" />/<asp:Label id="Label6" runat="server" /><br/> </font> <p/><p/> </body> </html>
開發與使用回傳 JavaBean(或者複雜的資料型態)的 AXIS 服務
就像之前文件的說明,我們不能任意的傳送 Java Objects,目前只建議 傳送 JavaBean。但是,傳送的 JavaBean 被 SOAP 訊息正確的表達之後 (也就是所謂的 serialize 或者 unmarshall), 我們必須注意 .NET 可以正確的解讀(也就是所謂的 deserialize 或者 unmarshall)。 由於 serialize/deserialize 的問題,我一直無法正確的讀取資料, 這一個問題困擾了我半天,終於在輸入正確的 keywords 之後,Goolge 大神 引導我到這一篇解答: Solved: null responses with interop between Axis service and .NET client。我們以範例來說明,要如何設計一個符合 JavaBean 的物件,讓 .NET 的 client 能夠正確的從 Axis service 讀取。
- 定義一個 JavaBean: DateBean.java 這個物件將會被 Axis service 傳送。
import java.util.*; public class DateBean { private int year, month, day; public DateBean() { Calendar now = Calendar.getInstance(); year = now.get(Calendar.YEAR); month = now.get(Calendar.MONTH); day = now.get(Calendar.DATE); } public int getYear() { return year; } public int getMonth() { return month + 1; } public int getDay() { return day; } public void setYear(int y) { year = y; } public void setMonth(int m) { month = m; } public void setDay(int d) { day = d; } }
- 定義服務:我們開發一個簡單的 BeanService.java。請特別留意,在 getDate() 這個方法回傳的是一個 DateBean 物件。
public class BeanService { public DateBean getDate() { DateBean d = new DateBean(); return d; } }
- 定義 wsdd 檔:這個檔案有幾個需要特別注意的地方:
- 我們必須增加一個 beanMapping 宣告,這個宣告就是下列範例中呈現暗紅色 的這一段。宣告中,明確的說明 DateBean 物件是一個以 Java 所寫出來的物件, 而且它的 QName 是 myNS:DateBean。
- 由於 AXIS 和 .NET 的轉換過程中,非常容易出現 namespace 對不上的情形, 而因為我們採用的是先開發 JavaBean 才由 AXIS 轉成相對的 WSDL 檔,又因為 AXIS 的預設行為就是產生一個名為 http://DefaultNamespace 的 targetNamespace,所以在為 DateBean 定義 namespace 的時候,我們就採用 http://DefaultNamespace;否則 .NET 客戶端所接受到的資料會是 0。 希望以後能夠找得到一種經由 AXIS 的設定,可以採用我們自訂的 namespace 的方式。
- 將 service 佈置好了以後,就可以開始開發客戶端程式。
- 開發 AXIS 的客戶端程式
- 過程跟之前所說的一樣,先利用 WSDL2Java 以及 service 的 WSDL 檔來產生 proxy 類別,然後把這些類別 import 到客戶端程式即可。由於我們在 WSDD 內定義 DateBean 的 namespace 是 DefaultNamespace,因此 WSDL2Java 會產生一個 DateBean.java 的程式在 DefaultNamespace 的子目錄內,所以程式中也記得 要把 DefaultNamespace import 進來。
- BeanClient.java
import java.rmi.*; import javax.xml.rpc.*; import _aa._bb._cc._dd.axis.services.BeanService.*; import DefaultNamespace.*; public class BeanClient { public static void main(String[] args) throws RemoteException, ServiceException { String URL = "http://aa.bb.cc.dd:8080/axis/services/BeanService"; BeanServiceService service = new BeanServiceServiceLocator(); BeanService call = service.getBeanService(); DateBean result = call.getDate(); System.out.println(result.getYear()); System.out.println(result.getMonth()); System.out.println(result.getDay()); } }
- 開發 .NET 的客戶端程式
- 過程跟之前所說的一樣,先利用 wsdl.exe 以及 service 的 WSDL 檔來產生 proxy 類別,然後依照 proxy 類別產生 .dll 檔。跟 Axis 不一樣的地方,.NET 的 wsdl.exe 並不會單獨在 DefaultNamespace 目錄內,產生 DateBean 的 proxy 類別。
- VBBeanClient.aspx
<%@Page Language="VB" %> <script runat="server"> Sub Page_Load(o As Object, e As EventArgs) ' 這個是 Axis 的 service Const URL as String ="http://hostname:8080/axis/services/BeanService" Dim service as New BeanServiceService() ' 設定服務的 URL service.Url = URL ' 呼叫服務的 getDate() Dim Answer As DateBean = service.getDate() Label1.Text = Answer.year Label2.Text = Answer.month Label3.Text = Answer.day End Sub </script> <HTML> <BODY> <h3 align="center">ASP.NET 2.0 SOAP Client</h3> <h4 align="center">Process JavaBeans</h4> <hr/> <font color="gray" size="5"> 年/月/日: <asp:Label id="Label1" runat="server" />/<asp:Label id="Label2" runat="server" />/<asp:Label id="Label3" runat="server" /><br/> </font> <p/> </body> </html>
- 執行後的畫面如下:
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="BeanService" provider="java:RPC" style="wrapped" use="literal"> <parameter name="className" value="BeanService" /> <parameter name="allowedMethods" value="*" /> <beanMapping qname="myNS:DateBean" xmlns:myNS="http://DefaultNamespace" languageSpecificType="java:DateBean" /> </service> </deployment>
開發與使用回傳 JavaBean 陣列的 AXIS 服務
- 定義 JavaBean:TimeBean.java
import java.util.*; public class TimeBean { private int hour, minute, second; public TimeBean() { Calendar now = Calendar.getInstance(); hour = now.get(Calendar.HOUR); minute = now.get(Calendar.MINUTE); second = now.get(Calendar.SECOND); } public int getHour() { return hour; } public int getMinute() { return minute; } public int getSecond() { return second; } public void setHour(int h) { hour = h; } public void setMinute(int m) { minute = m; } public void setSecond(int s) { second = s; } }
- 開發服務:TimeArray.java 該服務會產生兩個 TimeBean 物件,並以 物件陣列的方式回傳給 client 端。
import java.io.*; public class TimeArray { public TimeBean[] getTimeArray() throws Exception { TimeBean[] res = new TimeBean[2]; res[0] = new TimeBean(); // 只是為了確保兩個 TimeBean 的物件差一秒 Thread.sleep(1000); res[1] = new TimeBean(); return res; } }
- 定義 wsdd 檔
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="BeanArray" provider="java:RPC" style="wrapped" use="literal"> <parameter name="className" value="TimeArray" /> <parameter name="allowedMethods" value="*" /> <beanMapping qname="myNS:TimeBean" xmlns:myNS="http://DefaultNamespace" languageSpecificType="java:TimeBean" /> </service> </deployment>
- 將 service 佈置好了以後,就可以開始開發客戶端程式。
- 開發 AXIS 客戶端程式
- 過程跟之前所說的一樣,先利用 WSDL2Java 以及 service 的 WSDL 檔來產生 proxy 類別,然後將這些類別檔 import 到客戶端程式。
- BeanArrayClient.java
import java.rmi.*; import javax.xml.rpc.*; import _aa._bb._cc._dd.axis.services.BeanArray.*; import DefaultNamespace.*; public class BeanArrayClient { public static void main(String[] args) throws RemoteException, ServiceException { String URL = "http://aa.bb.cc.dd:8080/axis/services/BeanArray"; TimeArrayService service = new TimeArrayServiceLocator(); TimeArray call = service.getBeanArray(); TimeBean[] result = call.getTimeArray(); System.out.print(result[0].getHour() + ":"); System.out.print(result[0].getMinute() + ":"); System.out.println(result[0].getSecond()); System.out.print(result[1].getHour() + ":"); System.out.print(result[1].getMinute() + ":"); System.out.println(result[1].getSecond()); } }
- 開發 .NET 客戶端程式
- 過程跟之前所說的 一樣,先利用 wsdl.exe 以及 service 的 WSDL 檔來產生 proxy 類別,然後 在依照 proxy 類別產生 .dll 檔。
- 開發 .NET 客戶端程式
<%@Page Language="VB" %> <script runat="server"> Sub Page_Load(o As Object, e As EventArgs) ' 這個是 Axis 的 service Const URL as String ="http://hostname:8080/axis/services/BeanArray" Dim service as New TimeArrayService() ' 設定服務的 URL service.Url = URL ' 呼叫服務的 getDate() Dim Answer() As TimeBean = service.getTimeArray() Label1.Text = Answer(0).hour Label2.Text = Answer(0).minute Label3.Text = Answer(0).second Label4.Text = Answer(1).hour Label5.Text = Answer(1).minute Label6.Text = Answer(1).second End Sub </script> <HTML> <BODY> <h3 align="center">ASP.NET 2.0 SOAP Client</h3> <h4 align="center">Process JavaBean Arrays</h4> <hr/> <font color="gray" size="5"> 時/分/秒: <asp:Label id="Label1" runat="server" />/<asp:Label id="Label2" runat="server" />/<asp:Label id="Label3" runat="server" /><br/> 時/分/秒: <asp:Label id="Label4" runat="server" />/<asp:Label id="Label5" runat="server"/>/<asp:Label id="Label6" runat="server" /><br/> </font> <p/> </body> </html>