文章摘要:

本文使用JAX-WS2.2編譯webservice,並使用HttpUrlConnection的POST方式對wsdl傳送soap報文進行請求返回資料,

對錯誤Server returned HTTP response code: 500 的解決方法進行簡單分析。

問題描述:

由於課程需要博主需要自己寫一個webservice並且通過soap進行請求,

於是使用JAX-WS編譯了下面java程式碼生成webservice服務

生成webservice的java程式碼:

@WebService()
public class HelloWorld {
  @WebMethod
  public String sayHelloWorldFrom(String from) {
      System.out.println("getMessage.");
      String result = "Hello, world, from " + from;
      System.out.println(result);
      return result;
  }
  public static void main(String[] argv) {
    System.out.println("Service is running...");

    Object implementor = new HelloWorld ();
    String address = "http://localhost:9000/HelloWorld";
    Endpoint.publish(address, implementor);
  }
}
檢視webservice


在網上查到的一個方法就是通過HttpUrlConnection進行請求,這邊貼一下程式碼,應該很多人都有查到類似的方法

HttpUrlConnection請求實現程式碼:

public static void main(String[] args) throws Exception
    {
        String urlString = "http://localhost:9000/HelloWorld?wsdl";//自定義的wsdl服務
        URL url = new URL(urlString);
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();//開啟連線

        String xmlFile = "soap_xml\\soap.xml";//要傳送的soap格式檔案
        File fileToSend = new File(xmlFile);
        byte[] buf = new byte[(int) fileToSend.length()];// 用於存放檔案資料的陣列

        new FileInputStream(xmlFile).read(buf);

        //Content-Length長度會自動進行計算
        httpConn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
        httpConn.setRequestMethod("POST");
        httpConn.setDoOutput(true);
        httpConn.setDoInput(true);

        OutputStream out = httpConn.getOutputStream();

        out.write(buf);
        out.close();

        InputStreamReader is = new InputStreamReader(httpConn.getInputStream());
        BufferedReader in = new BufferedReader(is);
        String inputLine;
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream("result.xml")));// 將結果存放的位置
        while ((inputLine = in.readLine()) != null)
        {
            System.out.println(inputLine);
            bw.write(inputLine);
            bw.newLine();
        }
        bw.close();
        in.close();
        httpConn.disconnect();
    }

soap.xml程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <sayHelloWorldFrom>
            <arg0>
                撐撐
            </arg0>
        </sayHelloWorldFrom>
    </soap:Body>
</soap:Envelope>

這段程式碼是網上找的,並沒有錯誤,但是一執行就懵逼了,報了下面的錯誤

明明直接在瀏覽器檢視wsdl介面是可以訪問頁面,但是返回500錯誤

錯誤程式碼:

Exception in thread "main" java.io.IOException: Server returned HTTP response code: 500 for URL: http://localhost:9000/HelloWorld?wsdl
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1839)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440)
	at soap.HelloSoap.main(HelloSoap.java:38)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

錯誤語句指向

        InputStreamReader is = new InputStreamReader(httpConn.getInputStream());

而網上其他大部分文章都沒有給解決方法,或者給了其他的解決方法,我就在這邊分享一下這個問題的詳細解決方法。

解決流程(乾貨

首先應該確認具體的錯誤,通過下面的語句可以瞭解InputStream的錯誤詳情。

            InputStream is = httpConn.getErrorStream();    //通過getErrorStream瞭解錯誤的詳情,因為錯誤詳情也以XML格式返回,因此也可以用JDOM來獲取。
這個語句其實也是從請求的服務方取回的錯誤資訊,實質也是xml內容,用上面的BufferReader那一連串的語句解析出具體內容,然後輸出檢視,具體程式碼如下:  
            InputStream is = httpConn.getErrorStream();    //通過getErrorStream瞭解錯誤的詳情,因為錯誤詳情也以XML格式返回,因此也可以用JDOM來獲取。
            InputStreamReader isr = new InputStreamReader(is,"utf-8");
            BufferedReader in = new BufferedReader(isr);
            String inputLine;
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream("result.xml")));// 將結果存放的位置
            while ((inputLine = in.readLine()) != null) 
            {
                System.out.println(inputLine);
                bw.write(inputLine);
                bw.newLine();
                bw.close();
            }
            in.close();
錯誤詳情輸出如下:
<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Body>
        <S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
            <faultcode>
                S:Client
            </faultcode>
            <faultstring>
                找不到{}sayHelloWorldFrom的分派方法
            </faultstring>
        </S:Fault>
    </S:Body>
</S:Envelope>

在這邊就要注意了!這個xml檔案是第一個要點!

分析一下這段xml程式碼,可以看到有一個faultcode和faultstring,這就是解決這個問題的第一個突破口



這樣就一目瞭然了,錯誤程式碼中faultcode所含的是Client,說明傳遞的訊息有誤,服務端是沒有問題的。

於是從要傳送的soap中查詢錯誤。

這時候發現faultstring “{}找不到xxx的分派方法” 中有一個大括號,想不明白那個是什麼,剛好查看了一下web服務的頁面,就是最上面那張圖片,發現服務名和埠名那邊也有大括號{http://example/},然後也想起來之前看的其他xml程式碼中,要傳送的方法的那個節點中有寫名稱空間(xmlns),就是網上那個getWeatherByCityName的那個例子,大家應該都有看過,我就不再這邊費口舌了。

要是有仔細看我傳送的soap.xml的程式碼,就可以看到sayHelloWorldFrom那個節點沒有寫名稱空間,實在是查了這麼多程式碼網上都沒人提到,也沒有查到關於soap報文的編寫規範,還以為那邊可以不用寫,於是刪掉了。

問題就出在這,我重新試了一下,先把名稱空間隨便寫了個網址,再次執行後依然報錯,但是大括號中就有了名稱空間指向的網址了,於是把web服務裡的大括號裡的地址,即{http://example/}寫上去,總算是請求成功併成功返回資料!

修改後的soap.xml(就是添加了名稱空間)

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <m:sayHelloWorldFrom xmlns:m="http://example/">
            <arg0>
                撐撐
            </arg0>
        </m:sayHelloWorldFrom>
    </soap:Body>
</soap:Envelope>

大括號裡的地址其實就是wsdl中denfinition裡定義的targetNameSpace,具體解釋我貼個連結吧大家自己看吧,反正知道問題出在這邊總算是解決了。

其實出了bug第一反應確實應該是找Error資訊,這樣才好針對性的進行處理,如果本文的方法還沒辦法幫你解決的話,那還是建議大家靠前面檢視錯誤詳情的方法去進行bug的查詢~

主要內容到這邊結束啦,希望能幫到大家~

附錄

傳送httpUrlConnection的java程式碼
package soap;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created by cc on 2016/10/21.
 */
public class HelloSoap {

    public static void main(String[] args) throws Exception
    {
        String urlString = "http://localhost:9000/HelloWorld?wsdl";//wsdl文件的地址
        URL url = new URL(urlString);
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();//開啟連線

        //String soapActionString = "http://localhost:9000/HelloWorld/sayHelloWorldFrom";//Soap 1.1中使用

        String xmlFile = "soap_xml\\soap.xml";//要傳送的soap格式檔案
        File fileToSend = new File(xmlFile);
        byte[] buf = new byte[(int) fileToSend.length()];// 用於存放檔案資料的陣列

        new FileInputStream(xmlFile).read(buf);

        //Content-Length長度會自動進行計算
        httpConn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
        //httpConn.setRequestProperty("soapActionString",soapActionString);//Soap1.1使用 其實完全可以不需要
        httpConn.setRequestMethod("POST");
        httpConn.setDoOutput(true);
        httpConn.setDoInput(true);

        OutputStream out = httpConn.getOutputStream();

        out.write(buf);
        out.close();

        if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK)
        {
            InputStreamReader is = new InputStreamReader(httpConn.getInputStream());
            BufferedReader in = new BufferedReader(is);
            String inputLine;
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream("result.xml")));// 將結果存放的位置
            while ((inputLine = in.readLine()) != null)
            {
                System.out.println(inputLine);
                bw.write(inputLine);
                bw.newLine();
            }
            bw.close();
            in.close();
        }
        else{
            //如果伺服器返回的HTTP狀態不是HTTP_OK,則表示發生了錯誤,此時可以通過如下方法瞭解錯誤原因。
            InputStream is = httpConn.getErrorStream();    //通過getErrorStream瞭解錯誤的詳情,因為錯誤詳情也以XML格式返回,因此也可以用JDOM來獲取。
            InputStreamReader isr = new InputStreamReader(is,"utf-8");
            BufferedReader in = new BufferedReader(isr);
            String inputLine;
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream("result.xml")));// 將結果存放的位置
            while ((inputLine = in.readLine()) != null)
            {
                System.out.println(inputLine);
                bw.write(inputLine);
                bw.newLine();
                bw.close();
            }
            in.close();

        }
        httpConn.disconnect();
    }
}