2016年10月30日 星期日

WebService 基礎 ( WebService 一)

.WSDL:web service definition language
定義了web service的server端和client端請求和回應的格式,主要有5個,分別是types、message、portType、binding、service
一個web service對應唯一的wsdl文件

.SOAP:simple object access protocal
簡單的、基于HTTP和XML的、用在WEB上交換資料的
soap訊息:請求/回應訊息
http+xml片斷

.SEI:WebService EndPoint Interface
WebService server端用來處理請求的介面,java就是寫一個介面,上面加上@WebService

.CXF:Celtix + XFire
XFire原本就是webservice框架,可能有一些問題,修改bug後,加上Celtix變成新的框架,是apache開發的,用來開發webservice的server端和client端



※SEI和實作

package hello.test;
import javax.jws.WebService;
    
@WebService
public interface IHelloWS {
    public void hello();
}
--------------------
package hello.test;
import javax.jws.WebService;
    
@WebService(endpointInterface = "hello.test.IHelloWS")
public class HelloWS implements IHelloWS {
    @Override
    public void hello() {
        System.out.println("Hello WebService!");
    }
    
    public String add(Integer a, int b) {
        System.out.println("call add method!");
        return "Answer is " + (a + b);
    }
}




※啟動伺服器

package hello.test;
import javax.xml.ws.Endpoint;
    
public class MyServer {
    public static void main(String[] args) {
        String url = "http://localhost:8888/aaa/bbb/ccc";
        Endpoint.publish(url, new HelloWS());
        System.out.println("server is start!");
    }
}

※如有修改(SEI、SEI實作或這一隻測試類),必須重啟才會生效

※複製url到瀏覽器,最後加上「?wsdl」會有如下的xml格式,也就是wsdl

<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://test.hello/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://test.hello/" name="HelloWSService">
    <types>
        <xsd:schema>
            <xsd:import namespace="http://test.hello/" schemaLocation="http://localhost:8888/aaa/bbb/ccc?xsd=1"/>
        </xsd:schema>
    </types>
    
    <message name="hello">
        <part name="parameters" element="tns:hello"/>
    </message>
    
    <message name="helloResponse">
        <part name="parameters" element="tns:helloResponse"/>
    </message>
    
    <portType name="IHelloWS">
        <operation name="hello">
            <input wsam:Action="http://test.hello/IHelloWS/helloRequest" message="tns:hello"/>
            <output wsam:Action="http://test.hello/IHelloWS/helloResponse" message="tns:helloResponse"/>
        </operation>
    </portType>
    
    <binding name="HelloWSPortBinding" type="tns:IHelloWS">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
        <operation name="hello">
        
            <soap:operation soapAction=""/>
            
            <input>
                <soap:body use="literal"/>
            </input>
            
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>
    
    <service name="HelloWSService">
        <port name="HelloWSPort" binding="tns:HelloWSPortBinding">
            <soap:address location="http://localhost:8888/aaa/bbb/ccc"/>
        </port>
    </service>
</definitions>

※types最裡層還有一個import的網址,再開一個瀏覽器分頁,會發現它長如下的樣子

<xs:schema xmlns:tns="http://test.hello/" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="http://test.hello/">
    <xs:element name="hello" type="tns:hello"/>
    
    <xs:element name="helloResponse" type="tns:helloResponse"/>
    
    <xs:complexType name="hello">
        <xs:sequence/>
    </xs:complexType>
    
    <xs:complexType name="helloResponse">
        <xs:sequence/>
    </xs:complexType>
</xs:schema>

※格式內容下一篇再說,先說明SEI的部分

※@WebService有6個屬性,都是接收String,分別是
name、targetNamespace、serviceName、portName、wsdlLocation、endpointInterface

其中EI(也就是interface)只有3個合法值:name、targetNamespace、wsdlLocation,API有特別註明「This member-value is not allowed on endpoint interfaces.」,
其中name和targetNamespace屬性,如果EI和實作類的設定不同,會以EI為主
wsdlLocation只能設在SEI裡,如果實作類有設,會在執行時出「[failed to localize] cannot.load.wsdl(path/xxx.wsdl)」的錯


.endpointInterface
上面有用到,沒加也是可以run,但客戶端請求時有所不同,下面會說明
有加只會將有實作的部分暴露出來
沒加會將自己的方法暴露出來

一個方法會對應
兩個message
兩個schema(在import裡)
一個portType-->operation
一個binding-->operation

.targetNamespace:,指定targetNamespace,不加為「http://從右到左的包名/」,此例為「http://test.hello/」

.serviceName:指定serviceName,不加為「實作類別名+Service」,此例為「HelloWSService」

.name:指定portType的name屬性,不加為「介面名」,此例為「IHelloWS」
還會影響portType-->operation-->input/output的wsam:Action屬性,這個屬性是「targetNamespace名稱/name名稱/方法名稱+Request/Response」,input為Request;output為Response
,此例input為「http://test.hello/HelloWS/addRequest」;output為「http://test.hello/HelloWS/addResponse」

.portName:指定service-->port的name屬性,不加為「實作類別名+Port」,此例為「HelloWSPort」;
還會影響binding的name屬性,為「port的name屬性+Binding」,此例為「HelloWSPortBinding」

.wsdlLocation:指定wsdl的路徑

※還有@WebResult、@WebParam、@WebMethod,這三個annotation也很重要
@WebResult寫在方法上,name屬性可以設定xml的回傳值的名稱,不打為「return」
@WebParam寫在參數裡,name屬性可以設定xml的參數名稱,不打為「arg+數字(從0開始)」
@WebMethod 寫在方法上,有三個值operationName、action、exclude
operationName屬性可以設定message的name屬性和portType-->operation的name屬性,不打為方法名
exclude不能加在EI,預設為false,若為true,此方法不會暴露出來

※在definitions有很多屬性,其中targetNamespace和name下面程式的QName會用到,分別對應到第一和第二個參數



※Eclipse 的 Web Services Explorer

其實Eclipse內鍵也有一個瀏覽的工具,也類似於在網址上打XXX~?wsdl,裡面可以對wsdl做request、response,如下畫面

1.首先先切換到Java EE視圖
2.按Launch the Web Services Explorer,或者Run-->Launch the Web Services Explorer也是一樣
3.右上角有個WSDL Page
4.將剛剛網址的wsdl貼上後按Go,每按一次Go,左邊的Navigator都會出現一個你打的網址,就算你打的網址一樣也會出現
想刪除,只要在左框(Navigator)的右上角Clear即可


※JavaClientCallWebService.java

package xxx.ooo;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
    
public class JavaClientCallWebService {
    public static void main(String[] args) {
        Url url = null;
        try {
            url = new URL("http://localhost:8888/aaa/bbb/ccc?wsdl");
            QName qname = new QName("http://test.hello/", "HelloWSService");
            Service service = Service.create(url, qname);
            IHelloWS call = service.getPort(IHelloWS.class);
            call.hello();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}

※上面是用Eclipse內鍵的工具呼叫WebService,這裡是用寫程式的方式呼叫

※將wsdl裡面的definitions的targetNamespace和name放在QName的建構子裡即可

※如果實作類的@WebService沒有endpointInterface屬性,會報「javax.xml.ws.WebServiceException: 未定義的連接埠類型: {http://test.hello/}IHelloWS」或者「javax.xml.ws.WebServiceException: {http://test.hello/}HelloWSService 不是有效的連接埠. 有效的連接埠為: {http://test.hello/}HelloWSPort」,應該是一定要有interface了

※Eclipse會多一個Console,一個是伺服器端的;一個是用戶端的
但hello()是伺服器端的程式碼,所以「Hello WebService!」會印在伺服器端


※wsimport

在安裝java的路徑裡,例如C:\Program Files\Java\jdkXXX\bin,有一個wsimport工具(jre沒有)
它可以將我們剛剛寫的程式顯示出完整的程式碼,因為有很多預設值,所以上面的程式碼才會這麼少

此時在命令提示字元下使用這個命令:
wsimport -d d:\ws_practice -keep -verbose wsdl網址
-d後面接路徑,表示會將生成出來的.class檔放在這個路徑裡,不指定會在目前的目錄生成
-keep表示還會保留.java檔
-verbose表示生成的過程有畫面可以看



此時切換好路徑後,輸入 wsimport -keep http://localhost:8888/aaa/bbb/ccc?wsdl
新增一個project,package和之前一樣,然後將生成的程式碼複製進去,.java部分即可
後面的網址也可以是wsdl格式的檔案,例如將這段網址的結果複製到一個文字文件,然後下
wsimport -keep -d d:\xxx d:\wsdl_file.wsdl,也是可以,副檔不一定要wsdl,只要是純文字即可

※test.java

HelloWSService hws = new HelloWSService();
IHelloWS hw = hws.getHelloWSPort();
System.out.println(hw.getClass());
hw.hello();

※這支是使用wsimport幫我們生成的程式碼,測試用戶端的程式

※首先先用wsdl最下面的service的name屬性名稱,然後一定有個方法可以返回介面
service下有個port標籤,他的name屬性就是getXXX
所以以這個例子就是getHelloWSPort

※可以發現 hw.getClass()是用代理的



※TCP/IP Monitor

Eclipse有內鍵一個TCP/IP監控工具
首先將網址的wsdl存成一個檔案,然後將最下面的代碼改成自己監聽的port
<service name="HelloWSService">
    <port name="HelloWSPort" binding="tns:HelloWSPortBinding">
        <soap:address location="http://localhost:9999/aaa/bbb/ccc"/>
    </port>
</service>

※我是將8888改成9999

最後在Window-->Preferences,然後如下畫面設定:

※綠框是新改的port,紅框的部分只要打IP和port即可



設定後還要啟動才可以
如果要修改,必需先按左邊的設定,然後右邊的Stop,再按Edit

※程式執行和wsimport的test.java一樣,執行後會出現一個叫TCP/IP Monitor的View
如果沒出現,就要選Window-->Show View-->Other...後,會出現如下的畫面:


TCP/IP Monitor畫面如下:

左上的紅框是只要有一次請求就會出現一次,不管錯誤還是一樣的請求都會出現
左邊為Request;右邊為Response
選了XML就會出現最下面的SOAP格式的標籤,這個標籤在上面介紹的Web Services Exporer也有,如果選Web Browser會比較好看



※SOAP UI 工具

此工具的功能和Eclipse 內鍵的 Web Services Explorer一樣,可以分析wsdl的結構,但工作中還蠻流行的,在此介紹一下,這裡是官網,可到此下載

P.S 上面介紹的wsimport將wsdl轉成java代碼,可以在網路上搜尋「免費 webservice」,可以找到xxx.wsdl的網址,但不一定能成功轉成java代碼,因為有可能是其他語言產生出來的
但用Eclipse 內鍵的 Web Services Explorer 和 SOAP UI 都可以執行

※為了要測試有參數的情形,所以介面增加一個方法並實作
public String add(Integer a, int b);
--------------------
@Override
public String add(Integer a, int b) {
    System.out.println("call add method!");
    return "Answer is " + (a + b);
}

※安裝好後如下設定:

有以下四種方法可以開啟打xxx.wsdl的地方
1.按紅框的SOAP,
2.Project按右鍵--> New SOAP Project
3.File--> New SOAP Project
4.Ctrl+N




在紅框打上xxx.wsdl後,Project Name會自動代出值來,預設是帶最後的「/」之後的字串,可以改




最左邊可看到兩個方法,add是我剛才加的,按下去會出現右邊的圖
argX是打參數的地方,打完後按下綠色的三角形就會執行
最右邊就會出現結果,如果有在控制台打印字串,也會出現在 server 端的 console

沒有留言:

張貼留言