有 2 種主要的方式去設計一個 Web Service,就是 Top Down 和 Bottom Up,究竟有什麼分別呢?
Top Down 是由定義 Object (物件) 和 Interface (介面) 做起的,Object (物件) 你可以想像為傳遞數據的一個容器,而這個物件是由 XML Schema (XSD) 去描述的。Interface (介面) 則是描述這個 Web Service 提供什麼服務,服務需要傳入一個什麼 Object (物件) 和 返回一個什麼 Object (物件)等等描述,這些都是由 Web Services Description Language (WSDL) 去描述的。做好這 2 項工作後就需要製作和 XML Schema 描述一樣的 Object Class,亦要製作和 Web Services Description Language 描述一樣的 Functional Class。最後當然要寫程式式碼和放上 Application Server。
Bottom Up 就比較簡單了,是由 Object Class 和 Functional Class 先做起,做起後放上 Application Server 由 Application Server 自動產生出 XML Schema (XSD) 和 Web Services Description Language (WSDL)。不懂得 XML Schema 和 Web Services Description Language 也一樣可以製作出一個 Web Service。
最好當然是對 XML Schema 和 Web Services Description Language 有一點認識。
XML Schema: http://www.w3schools.com/schema/default.asp
Web Services Description Language:
http://www.w3schools.com/WSDL/default.asp
首先當然講解一下最簡單的 Bottom Up 方法,首先要在 IDE 建立一個 WAR project,加入一個 Java Class:
package sample.ws;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService(
name = "CalculaterService",
serviceName = "CalculaterService",
portName = "CalculaterServicePort",
targetNamespace = "http://pro.ctlok.com/CalculaterService/")
public class CalculaterService {
@WebMethod
public int add(int i, int k) {
return i + k;
}
@WebMethod
public int subtract(int i, int k) {
return i - k;
}
}
就這樣完成了一個最簡單的 Web Service。
@WebService(
name = "CalculaterService",
serviceName = "CalculaterService",
portName = "CalculaterServicePort",
targetNamespace = "http://pro.ctlok.com/CalculaterService/")
用於通知 Java EE 容器識別這個 Class 是一個 Web Service。
- name: 是在 Application Server 上的名稱
- serviceName: 是在 URL 和 WSDL 上顯示的名稱
- portName: 是在 WSDL 上顯示的名稱
- targetNamespace : 是在 WSDL 上顯示的命名空間,令 XML 內的名稱不會相同
@WebMethod
是指定這個 Method 是 Web Service 的其中一個 Function。
CalculaterService 亦可以是一個 EJB Session Bean:
package sample.ws;
@Local
public interface CalculaterService{
public int add(int i, int k);
public int subtract(int i, int k);
}
package sample.ws;
import javax.jws.WebMethod;
import javax.jws.WebService;
@Stateless
@WebService(
name = "CalculaterService",
serviceName = "CalculaterService",
portName = "CalculaterServicePort",
targetNamespace = "http://pro.ctlok.com/CalculaterService/")
public class CalculaterServiceBean implements CalculaterService{
@WebMethod
public int add(int i, int k) {
return i + k;
}
@WebMethod
public int subtract(int i, int k) {
return i - k;
}
}
放上 Application Server 後使用瀏覽器開啟: http://localhost/CalculaterService?wsdl 會顯示出以下的 WSDL
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="CalculaterService" targetNamespace="http://pro.ctlok.com/CalculaterService/"
xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://pro.ctlok.com/CalculaterService/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<types>
<xsd:schema>
<xsd:import namespace="http://pro.ctlok.com/CalculaterService/"
schemaLocation="CalculaterService_schema1.xsd" />
</xsd:schema>
</types>
<message name="addResponse">
<part name="parameters" element="tns:addResponse">
</part>
</message>
<message name="add">
<part name="parameters" element="tns:add">
</part>
</message>
<message name="subtractResponse">
<part name="parameters" element="tns:subtractResponse">
</part>
</message>
<message name="subtract">
<part name="parameters" element="tns:subtract">
</part>
</message>
<portType name="CalculaterService">
<operation name="add">
<input message="tns:add">
</input>
<output message="tns:addResponse">
</output>
</operation>
<operation name="subtract">
<input message="tns:subtract">
</input>
<output message="tns:subtractResponse">
</output>
</operation>
</portType>
<binding name="CalculaterServicePortBinding" type="tns:CalculaterService">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="add">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="subtract">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>
<service name="CalculaterService">
<port name="CalculaterServicePort" binding="tns:CalculaterServicePortBinding">
<soap:address
location="http://localhost/CalculaterService" />
</port>
</service>
</definitions>
Application Server 除了自動產生出 WSDL 外,亦會產生出 XML Schema。
使用瀏覽器開啟: http://localhost/CalculaterService/CalculaterService_schema1.xsd 會顯示出以下的 XML Schema:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://pro.ctlok.com/CalculaterService/" version="1.0"
targetNamespace="http://pro.ctlok.com/CalculaterService/">
<xs:element name="add" type="tns:add"></xs:element>
<xs:element name="addResponse" type="tns:addResponse"></xs:element>
<xs:element name="subtract" type="tns:subtract"></xs:element>
<xs:element name="subtractResponse" type="tns:subtractResponse"></xs:element>
<xs:complexType name="add">
<xs:sequence>
<xs:element name="arg0" type="xs:int"></xs:element>
<xs:element name="arg1" type="xs:int"></xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="addResponse">
<xs:sequence>
<xs:element name="return" type="xs:int"></xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="subtract">
<xs:sequence>
<xs:element name="arg0" type="xs:int"></xs:element>
<xs:element name="arg1" type="xs:int"></xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="subtractResponse">
<xs:sequence>
<xs:element name="return" type="xs:int"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
看到這是資料即是成功部署到 Web Service,接下來使用 soapUI 來模擬 Web Service Client。
soapUI 下載地址: http://sourceforge.net/projects/soapui/files/
請選擇安裝程式去下載,完成安裝後打開 soapUI:
- File -> New soapUI Project
- 在 Project name 輸入 localhost 後按 OK
- 在 Project 上按滑鼠加鍵 -> Add WSDL
- 在 WSDL Location 輸入 http://localhost/CalculaterService/CalculaterService.wsdl
現在應該看得到 2 個 Method 名稱,分別為 add 和 subtract。
打開 add 的 Request 1 左邊顯示以下資料:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ws="http://pro.ctlok.com/CalculaterService/">
<soapenv:Header />
<soapenv:Body>
<ws:add>
<arg0>?</arg0>
<arg1>?</arg1>
</ws:add>
</soapenv:Body>
</soapenv:Envelope>
改為:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ws="http://pro.ctlok.com/CalculaterService/">
<soapenv:Header />
<soapenv:Body>
<ws:add>
<arg0>1</arg0>
<arg1>2</arg1>
</ws:add>
</soapenv:Body>
</soapenv:Envelope>
然後傳送出去,右邊會顯示出以下 XML 即代表成功:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<dlwmin:addResponse xmlns:dlwmin="http://pro.ctlok.com/CalculaterService/">
<return>3</return>
</dlwmin:addResponse>
</soapenv:Body>
</soapenv:Envelope>
其實在 Web Service 的 Function 不一定要傳入或返回基本型態 (short, int, long, float, double, char, byte, boolean),可以傳入或返回 Object (物件),例如:
Object Class:
package sample.ws;
public class Info{
private String firstName;
private String lastName;
public String getFirstName(){
return firstName;
}
public String getLastName(){
return lastName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
}
public void setLastName(String lastName){
this.lastName = lastName;
}
}
Web Service:
@WebMethod
public String sayHello(Info info) {
return "Hello! " + info.getFirstName()
+ " " + info.getLastName();
}
@WebMethod
public Info getInfo(int id){
return findInfo(id);
}
有時間會再講解 Top Down 的做法。
相關書籍: