【WebService】帶你走進webservice的世界

1. webservice是啥

  準確的來說,webservice不是一種技術,而是一種規範。是一種跨平臺,跨語言的規範,用於不同平臺,不同語言開發的應用之間的交互。 
  舉個例子,比如在Windows Server服務器上有個C#.Net開發的應用A,在Linux上有個Java語言開發的應用B,現在B應用要調用A應用,或者是互相調用,用於查看對方的業務數據,就需要webservice的規範。 
  再舉個例子,天氣預報接口。無數的應用需要獲取天氣預報信息,這些應用可能是各種平臺,各種技術實現,而氣象局的項目,估計也就一兩種,要對外提供天氣預報信息,這個時候,如何解決呢?webservice就是出於以上類似需求而定義出來的規範。 
  我們一般就是在具體平臺開發webservice接口,以及調用webservice接口,每種開發語言都有自己的webservice實現框架。比如Java 就有 Apache Axis1、Apache Axis2、Codehaus XFire、Apache CXF、Apache Wink、Jboss RESTEasyd等等。其中Apache CXF用的比較多,它也可以和Spring整合。

2. 重溫socket

在分析如何調用webservice前,先來回憶一下傳統的socket是如何通信的,這樣更容易理解ws。

2.1 基於socket創建web服務

爲什麼要使用socket呢?看一下下面的原理圖: 
這裏寫圖片描述 
  從圖中可以看出,程序A和程序B之間是無法實現直接調用的,那麼現在A需要訪問B的話,A即創建一個socket並制定B機器的端口號,在此之前B已經在本機創建好了socket等待用戶來連接,A和B連接成功後,即可向B發送請求獲取數據了,這很好理解,爲了回憶一下socket的創建和使用,下面先寫一個簡單的socket通信的demo,服務端可以將小寫字母轉大寫。

2.2 經典的socket服務

客戶端:

/**
 * @Description Socket客戶端,用來發送給服務端請求
 * @author Ni Shengwu
 *
 */
public class SocketClient {

    public static void main(String[] args) throws Exception {
        Scanner input = new Scanner(System.in);

        // 1: 創建一個基於TCP協議的socket服務,在建立對象時,要指定連接服務器和端口號
        Socket sc = new Socket("127.0.0.1", 9999);
        // 2: 通過建立的Socket對象獲取Socket中的輸出流,調用getOutStream方法
        OutputStream out = sc.getOutputStream();

        System.out.println("請輸入要轉化的字母:");
        String initData = input.next();//獲取控制檯的輸入

        // 3: 寫入到Socket輸出流中
        out.write(initData.getBytes());
        System.out.println("等待服務器端返回數據");

        // 4: 通過建立的Socket對象獲取Socket中的輸入流,輸入流會接受來自服務器端數據
        InputStream in = sc.getInputStream();
        byte[] b = new byte[1024];

        // 5: 獲取輸入字節流的數據,注意此方法是堵塞的,如果沒有獲取數據會一直等待
        int len = in.read(b);
        System.out.println("返回的結果爲:" + new String(b, 0, len));

        // 關閉Socket
        out.close();
        in.close();
        sc.close();
        input.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

服務端:

/**
 * @Description Socket服務端,用來接收客戶端請求,實現轉大寫功能
 * @author Ni Shengwu
 *
 */
public class SocketServer {

    public static void main(String[] args) throws Exception {

        // 1:建立服務器端的tcp socket服務,必須監聽一個端口
        ServerSocket ss = new ServerSocket(9999);
        while(true) {
            System.out.println("等待客戶端請求……");

            // 2: 通過服務器端的socket對象的accept方法獲取連接上的客戶端對象,沒有則堵塞,等待
            Socket socket = ss.accept();
            System.out.println("握手成功……");

            // 3: 通過輸入流獲取數據
            InputStream input = socket.getInputStream();
            byte[] b = new byte[1024];
            int len = input.read(b);
            String data = new String(b, 0, len);
            System.out.println("客戶端數據爲:" + data);

            // 4: 通過服務器端Socket輸出流,寫數據,會傳送到客戶端Socket輸入流中
            OutputStream out = socket.getOutputStream();
            out.write(data.toUpperCase().getBytes());

            // 5: 關閉socket
            out.close();
            input.close();
            socket.close();

        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

  這個demo很簡單,先開啓服務端的程序,在那等待,然後開啓客戶端程序,如果在控制檯輸入hello過去,就會從服務端返回一個HELLO回來,這說明socket通信是成功的。

2.3 web程序訪問socket service

  上面經典的demo是在本地寫的兩個java程序,我們現在的很多項目都是web項目,也就是通過瀏覽器來交互的,我們來看下通過瀏覽器的方式如何來訪問socketService服務。服務端還是使用上面的那個java程序,客戶端我改成瀏覽器請求,新寫一個jsp如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>   
  </head> 
  <body>
    <form action="http://127.0.0.1:9999" method="post">
        <input type="text" name="sname">
        <input type="submit" value="提交">
    </form>
  </body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

  注意看action的請求地址,包括端口要和服務端的一樣,這樣當我們提交的時候就可以訪問上面的服務端程序了。看一下服務端的運行結果: 
這裏寫圖片描述 
  可以看到,web程序確實和服務端握手成功了,而且數據也是可以傳過去的,sname=hello,包括一些Http信息都可以傳過去,但是我們再來看看服務端返回給瀏覽器的數據是啥: 
這裏寫圖片描述 
  我用的是chrome瀏覽器,其他瀏覽器可能還沒有數據,這都有可能,但是從數據中來看,它只是單純的把所有信息全部轉成了大寫……而且它也沒有Http的返回格式,也就是說,我所需要的就是個大寫的HELLO即可,所以這是有問題的。 
  所以可以總結一下:不同的協議其實也是支持Socket通信的。 web程序可以調用socket請求,但是由於協議不同,因此在處理的時候要過濾http的協議格式,返回的時候還需要添加 http返回的格式,否則就會出現問題,可想而知,如果還要處理協議格式,是很麻煩的。 
  所以到這裏,基本上就理解了爲什麼傳統的socket無法滿足需求了,其實除了上面的弊端外,還有其他的弊端,比如如果參數一多,就不好維護等等,這裏就不多舉例了。

3. 調用已發佈的WebService

  關於webservice本身,我就不做過多的描述了,在最上面也有簡單介紹,既然傳統的socket通信無法滿足,那麼下面開始來調用已發佈的ws,真正走進ws的世界。 
  有一個站點:http://www.webxml.com.cn,是上海的一家公司做的,上面提供了很多ws服務,其中有一個查詢號碼歸屬地的功能,我們用它來做測試。先來在它們的站點中測試一下,然後再在本地寫程序來調用這個ws服務獲取查詢結果。

看一下站點上的查詢: 
這裏寫圖片描述 
進入手機號碼歸屬地查詢web服務後, 
這裏寫圖片描述 
選擇getMobileCodeInfo,即可進入查詢頁面了, 
這裏寫圖片描述 
  調用後就會出現<string xmlns="http://WebXml.com.cn/">18312345678:廣東 深圳 廣東移動全球通卡</string>的結果。這就是調用ws的結果,接下來我們在程序中來調用這個ws。

3.1 get請求方式

  在java程序中如果要發送http請求,需要使用HttpClient工具。HttpClient 是 Apache Jakarta Common 下的子項目,可以用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,並且它支持 HTTP 協議最新的版本和建議。 
  爲什麼要使用HttpClient工具呢?因爲原生態的Socket基於傳輸層,現在我們要訪問的WebService是基於HTTP的屬於應用層,所以我們的Socket通信要藉助HttpClient發HTTP請求,這樣格式才能匹配。

/**
 * @Description get方式請求
 * @param number
 * @throws Exception
 */
public void get(String number) throws Exception {
    //HttpClient:在java代碼中模擬Http請求
    // 創建瀏覽器對象
    HttpClient client = new HttpClient();
    // 填寫數據,發送get或者post請求
    GetMethod get = new GetMethod("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx"
            + "/getMobileCodeInfo?mobileCode=" + number + "&userID=");
    // 指定傳輸的格式爲get請求格式
    get.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    // 發送請求
    int code = client.executeMethod(get);
    System.out.println("Http:狀態碼爲:" + code);

    String result = get.getResponseBodyAsString();
    System.out.println("返回的結果爲:" + result);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

  從程序中可以看出,請求的主機是ws.webxml.com.cn,這些url在ws提供方的網站上都有,我們只需要寫對即可請求ws了,在main方法中調用一下該方法即可在控制檯獲取結果。

3.2 post請求方式

請求的過程都一樣,只是url和傳輸格式不同而已,修改一下相應的地方即可,

/**
 * @Description post方式請求
 * @param number
 * @throws Exception
 */
public void post(String number) throws Exception {
    //HttpClient:在java代碼中模擬Http請求
    // 創建瀏覽器對象
    HttpClient client = new HttpClient();
    // 填寫數據,發送get或者post請求
    PostMethod post = new PostMethod("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo");

    // 指定傳輸的格式爲默認post格式
    post.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");     
    // 傳輸參數
    post.setParameter("mobileCode", number);
    post.setParameter("userID", "");

    // 發送請求
    int code = client.executeMethod(post);
    System.out.println("Http:狀態碼爲:" + code);

    String result = post.getResponseBodyAsString();
    System.out.println("返回的結果爲:" + result);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

3.3 SOAP方式請求

這也是在用的多的方式,它有兩個版本soap1.1和soap1.2,jdk1.7及以上纔可以使用soap1.2。

/**
 * @Description soap post方式請求,但是傳輸的數據爲xml格式,有利於數據的維護
 * @param number
 * @throws Exception
 */
public void soap(String number) throws Exception {
    //HttpClient:在java代碼中模擬Http請求
    // 創建瀏覽器對象
    HttpClient client = new HttpClient();
    // 填寫數據,發送get或者post請求
    PostMethod post = new PostMethod("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx");

    // 指定傳輸的格式爲xml格式
    post.setRequestHeader("Content-Type", "application/soap+xml;charset=utf-8");
    // 傳輸xml,加載soap.txt
    post.setRequestBody(new FileInputStream("E:/github/client/src/soap.txt"));  
    // 發送請求
    int code = client.executeMethod(post);
    System.out.println("Http:狀態碼爲:" + code);

    String result = post.getResponseBodyAsString();
    // 如果採用的是soap,則返回的數據也是基於xml的soap格式
    System.out.println("返回的結果爲:" + result);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

  由於soap方式需要向服務端發送xml,所以我們可以實現寫好一個txt文檔,裏面是xml的數據,這個模板ws提供方會提供,我們需要寫好即可:

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <soap12:Body>
    <getMobileCodeInfo xmlns="http://WebXml.com.cn/">
      <mobileCode>18312345678</mobileCode>
      <userID></userID>
    </getMobileCodeInfo>
  </soap12:Body>
</soap12:Envelope>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

  上面這些方式推薦使用soap的方式,不過本質上還是http方式調用,只是調用的時候可以傳輸xml數據而已。而且HttpClient是Java的調用http協議的解決方案,但是不能保證其它語言也擁有類似的工具。所以ws推薦的方案是使用wsimport命令。這也是下面分析的重點。

3.4 使用wsimport

  每個ws都會有一個WSDL,WSDL即WebService Description Language – Web服務描述語言。它是通過XML形式說明服務在什麼地方-地址。通過XML形式說明服務提供什麼樣的方法 – 如何調用。我們可以通過這個WSDL來獲取和這個ws有關的信息,包括class和java代碼。關於這個WSDL後面我再具體分析,這一節先來看一下如何使用。 
  wsimport是一個命令,jdk1.6及以上纔可以使用,ws針對不同的語言都會有個wsimport命令,我們可以在自己安裝的jdk的bin目錄下找到這個wsimport.exe,正因爲有了這個,所以我們可以在命令行中使用wsimport命令。怎麼使用呢? 
  每個ws都會有一個WSDL,就拿上面的歸屬地查詢服務來說,上面第二張圖上面有個服務說明,點開就可以看到WSDL,當然也可以直接訪問瀏覽器上的url來訪問這個WSDL,即xml文檔。如下: 
這裏寫圖片描述
  目前只需要複製一下那個url即可,然後打開命令提示符窗口,隨便進入一個目錄下(該目錄要保存等會生成的和ws相關的文件,自己事先建一個即可),運行 
wsimport http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL 
  就會生成相應的javabean,當然了,是.class文件,但是我們不想要class文件,我們想要java文件,所以可以使用如下命令: 
wsimport -s http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL 
  這樣不僅生成了class文件,還生成了java文件,如果我們想要在固定的包下生成這些文件,等會方便直接拷貝到項目裏,可以使用下面的命令: 
wsimport -s . -p ws.client.c http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL 
  這樣就會在目錄ws/client/c/下生成所需要的class和java代碼,然後我們刪掉class文件,直接拷貝ws目錄到工程中即可,如下(_Main是我自己寫的,用來調用使用的): 
這裏寫圖片描述 
  這樣就有了號碼歸屬地查詢這個ws服務相關的API了,這是通過官方的WSDL來生成的,然後我們如何在自己的項目中使用呢?我新寫一個_Main.java文件,直接使用這些API即可,如下:

public class _Main {
    public static void main(String[] args) {

        // 獲取一個ws服務
        MobileCodeWS ws = new MobileCodeWS();
        // 獲取具體的服務類型:get post soap1.1 soap1.2
        MobileCodeWSSoap wsSoap = ws.getMobileCodeWSSoap();
        String address = wsSoap.getMobileCodeInfo("18312345678", null);
        System.out.println("手機歸屬地信息爲:" + address);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

  這樣就很方便了,現在已經完全沒有了上面那種連接啊,設置地址啊等等,直接封裝好了,我直接調用這些API即可調用遠程的webservice。這也是官方推薦的一種方法,當然我們也可以將生成的class文件打包成jar放到工程中。運行一下這個main方法後,也直接返回歸屬地,沒有那些標籤的東西了,這纔是開發中所需要的東西。 
  到這裏基本已經會調用webservice了,最後再簡單總結一下,ws中這個WSDL很重要,這裏面用xml描述了該ws的信息,所以我們可以通過解析WSDL來獲取該ws相關的API,然後在自己的項目中調用這些API即可調用該ws。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章