WebService監聽工具:
既然WebServie也是通過HTTP進行通信的,能不使用HTTPWatch來獲取它的請求過程呢?
我們的代碼不僅僅是向服務器發送的HTTP協議,更具體的說應該叫SOAP協議,它是WebService進行通信的基
爲了監控攔截請求頭和響應頭的具體數據,我們使用TCP/IP Monitor來攔截請求和響應的完整過程。
1:簡單的說,SOAP就是在HTTP的基礎上傳輸XML數據,以實現遠程調用。
因爲HTTP和XML格式的數據已經被廣泛的應用。而SOAP又架構在這兩種技術之上,所以WebService爲什麼會流行也就不難理解了。
2:老生常談,無論你的服務端是什麼語言書寫的,只要接收SOAP協議的XML數據,並返回SOAP協議的XML數據,就可以被任何語言調用。
3、以下是通過純ajax向服務器發送XML數據並解析的代碼:
<%@page language="java" import="java.util.*"pageEncoding="UTF-8"%>
<!DOCTYPEHTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<p>通過Ajax向服務器發送XML數據</p>
<button onclick="test1();">Ajax</button>
</body>
<script type="text/javascript">
varhttp;
functiontest1(){
if(window.XMLHttpRequest){
http =new XMLHttpRequest();
}else{
http =new ActiveXObject("Microsoft.XMLHttp");
}
varurl= "One";
http.open("POST",url,true);
http.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
http.onreadystatechange=function(){
if(http.readyState==4){
if(http.status==200){
//返回JSON
varjson= http.responseText;
varperson= eval("("+json+")");
alert("人員名單:"+person.name);
}
}
};
http.send("<?xmlversion='1.0' encoding='utf-8'?>" +
"<user><name>xx</name><name>張三</name></user>");
}
</script>
</html>
---
服務器使用jaxp進行解析
packagecn.itcast;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.PrintWriter;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjavax.xml.parsers.DocumentBuilder;
importjavax.xml.parsers.DocumentBuilderFactory;
importorg.w3c.dom.Document;
importorg.w3c.dom.Element;
importorg.w3c.dom.NodeList;
/**
* 接收XML數據使用jaxp進行解析
* @author
*/
publicclass One extends HttpServlet{
public void doGet(HttpServletRequestrequest, HttpServletResponseresponse)
throws ServletException,IOException{
doPost(request,response);
}
public void doPost(HttpServletRequestrequest, HttpServletResponseresponse)
throws ServletException,IOException{
//獲取輸入流,如果在輸出,請在組成String時使用UTF-8
InputStreamin = request.getInputStream();
String names = "";
// 使用JAXP解析
try {
DocumentBuilderFactoryfactory = DocumentBuilderFactory
.newInstance();
DocumentBuilderbuilder = factory.newDocumentBuilder();
Document dom= builder.parse(in);
NodeList nl = dom.getElementsByTagName("name");
for(inti=0;i<nl.getLength();i++){
Element el = (Element)nl.item(i);
String name = el.getTextContent();
System.err.println(">>:"+name);
names +=name;
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage(),e);
}
response.setContentType("text/xml;charset=UTF-8");
PrintWriterout = response.getWriter();
out.print("{\"name\":\""+names+"\"}");
}
}
-----以下通過jQuery+Dom4j實現發XML數據--------
<scripttype="text/javascript"src="js/jquery-1.6.2.js"></script>
<scripttype="text/javascript">
$(function(){
$("#jq").click(function(){
varxml = "<?xml version='1.0' encoding='UTF-8'?>" +
"<user><name>王健A</name><name>張三</name></user>";
alert(xml);
$.ajax({
url:'Two',
type:'post',
dataType:'json',//設置返回的數據類型
data:xml,//直接發xml數據
contentType:'application/x-www-form-urlencoded',
success:function(data){
alert("返回的信息是:"+data.name);
},
complete:function(http,textStatus){
alert("over..."+textStatus);
}
});
});
});
</script>
----------------------------
packagecn.itcast;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.PrintWriter;
importjava.util.Iterator;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.dom4j.Document;
importorg.dom4j.Element;
importorg.dom4j.io.SAXReader;
/**
* JQuery+Dom4j
* @author xx
*/
publicclass Two extends HttpServlet{
public void doPost(HttpServletRequestrequest, HttpServletResponseresponse)
throws ServletException,IOException{
String result = "";
//中文newString(b,0,len,"UTF-8")
InputStreamin = request.getInputStream();
//以下用dom4j解析
SAXReadersax = new SAXReader();
try {
Document dom= sax.read(in);
Element root = dom.getRootElement();
Iterator<Element> it = root.elementIterator();
while(it.hasNext()){
String nm = it.next().getStringValue();
System.err.println(nm);
result+=nm;
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage(),e);
}
response.setContentType("text/html;charset=UTF-8");
PrintWriterout = response.getWriter();
//返回json數據
out.print("{\"name\":\""+result+"\"}");
}
}
WS Explorer工具的使用:- web服務瀏覽器
1、將wsdl文件,保存成本地一樣可以通過此工具訪問遠程服務。爲file:///D:/abc.xml
在MyEclipse中調用WebService可以快速驗證你的服務器端程序,從而省去了自己書寫客戶端。
1:這是發出的消息格式:
<soapenv:Envelopexmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:q0="http://itcast.com/"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-<soapenv:Body>
-<q0:sayHi>
<arg0>zhangsan同學</arg0>
</q0:sayHi>
</soapenv:Body>
</soapenv:Envelope>
2:以下是接收到的XML格式
<S:Envelopexmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
-<S:Body>
-<ns2:sayHiResponse xmlns:ns2="http://itcast.com/">
<return>你好:zhangsan同學,當前時間是:2011-05-07 10:15:20</return>
</ns2:sayHiResponse>
</S:Body>
</S:Envelope>
3:上面的1和2就是SOAP(SimpleObject Access Protocol)簡單對像訪問協議的格式。
1:我們已經說過了,WebService是通過向服務器發出XML格式的數據實現遠程調用,然後服務器也返回XML數據給客戶端,那麼這個XML是什麼格式的呢?
下面我將使用MyEclipse中的WebServiceExplorer工具向我們的WebService發起請求,並查看它的XML數據格式。
2:通過HttpWatchprofession Edition只可以看到獲取wsdl文檔的具體信息。
且必須安裝HttpWatchprofession Edition版本的纔可以,如果是Basic版本的,將不會看到Stream(數據流)信息。
3、使用MyEclipse的TCP/IPMonitor這個工具來查看具體的請求過程。
TCP/IPMonitor不僅可以看到SOAP數據,還可以獲取HTTP請求和接收的頭信息。
3.1、此工具位於:window>showview>other>MyEclipseCommon(常用工具)>TCP/IPMonitor
3.2、此工具,相當於一個代理商,啓動後它將監聽本地的某個端口,然後再將請求轉發給指定的目標IP和端口。
獲取到數據後,再將數據原封不動的返回給客戶。在客戶看來,永遠首先訪問的都應該是這個代理,否則
我們將看不到數據傳輸的過程。
3.3、配置選項:
在打開的TCP/IPMonitor界面上:viewMenu(右上方向下的小箭頭)>Properties>Add(右側添加)
設置成以下屬性:
步1:
localmonitoring port(監聽本地的端口號):9876,隨意設置一個4位的端口號,一會將通過http://127.0.0.1:9876的形式訪問
hostname(要監聽的服務器,如www.baidu.com):127.0.0.1–因爲本機發布了一個WebService所以監聽本機IP.也可以是任意的主機。
Port(要監聽的目標服務器的端口):6666 - 因爲我們發佈的WebService爲http://127.0.0.1:6666/helloworld所以,6666是需要監聽的端口號。
Type(監聽的類型):
--TCP/IP :將使用原始地址繼續訪問下一個請求,如用戶輸入:http://127.0.0.1:9876/helloworld?wsdl此時將返回wsdl服務訪問地址同前。
-- HTTP :將使用目標地址繼續訪問下一個請求。如用戶輸入: http://127.0.0.1:9876/helloworld?wsdl在請求方法時將使用http://127.0.0.1:6666/helloworld來訪問sayHi方法。此
種方式將不再會被代理。因爲已經不是正在監聽的端口號了。
在監聽類型處,我們選擇TCP/IP。
建議同學們,將兩種方式都設置一下,然後通過在地址欄輸入:http://127.0.0.1:9876/helloworld?wsdl,在返回的wsdl文件中
查看:<soap:addresslocation=“http:……”/>處地址的變化情況。
timeout:設置訪問不成功的連接時間,保持爲0,即不設置。
在設置好後,點OK按扭,然後再點右方的Start按扭,監聽便已經啓動。
步2:
在MyEclipse的WebService上配置WSDLURL爲:http://127.0.0.1:9876/helloworld?wsdl,注意使用的是MyEclipseTCP/IP Monitor的端口。而不是直接去訪問我們發佈的
http://127.0.0.1:6666/helloworld?wsdl
在WebServiceExplorer上請求sayHi方法,傳遞參數,並查看http頭信息和發送及返回的xml信息。
無論是MyEclipseWebServiceExplorer,Httpwatch,還是TCP/IPMonitor都是一些輔助工具。雖然對編寫代碼沒有什麼用處,但對我如何監控問題,發現問題的能力有所幫助。
更能從工具的使用中,發現代碼的問題進行調整或改進。
看董wsdl文件,能幫助我們順利弄清如何調用一個WebService,以及它有哪些類的哪些方法可以調用。
1、SOAPBinding(style=Style.DOCUMENT或Stype.RPC)指定了消息的格式,即相互交流以語言。此值默認爲DOCUMENT。並在wsdl文
件的soap:binding….type上出現。請大家注意觀察。
(目前我的使用的WebService規範是基於jax-ws的,而rpc是基於jax-rpc的,已經過時。)
以下是wsdl文檔的部分代碼:
<soap:bindingtransport="http://schemas.xmlsoap.org/soap/http"style="document"/>
Style指定了消息格式
Transport的最後部分指定了使用什麼樣的協議。此處定義爲http協議。
所以,binding部分,定義瞭如何調用,使用什麼樣的協議和消息格式,有什麼樣的方法可以調用。
portType又指定了一個可以調用的Java類,
請看本目錄下的wsdl文件的圖示說明!03_WSDL文件分析.bmp
--types定義了命名空間的信息,其中namespace是倒置的包名。
schemaLocation是xsd地址信息,可以拷貝此地址,並查看xsd的定義
。
開始的二行註釋是Jax-ws的版本,及是由哪個jdk生成的。
1、上面所有的內容,都是由系統發佈時自動生成的,那麼如何纔可以修改這個文檔呢?
修改wsdl文件的內容:
1、另有:SOAPBinding-指定WebService到SOAP協議的影射關係?
使用不同版本的Jdk對發佈ws的影響.
1.5不支持.
1.6.0_20前版本必須使用完整註解.
1.6.0_21以後可以只使用@WebService對類進行註解.
2、關於namespace約定名的說明,@WebService(targetNameSpace=…..)
targetNamespace
定義導出的服務接口的名域(namespace),默認是倒置的服務接口Java包名。如demo.cxf.UserService的名域將會是http://cxf.demo/
1:WebService的註解都位於javax.jws包下。
主要包含以下幾個註解(直接查看JDK文檔,關於它裏面的配置屬性也直接看JDK6的文檔。)
我們只討論以下加個註解:
WebMethod WebParam WebResult WebService
2:以下是加了註解的示例:
package com.itcast;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.WebParam.Mode;
import javax.xml.ws.Endpoint;
/**
* 一個加了很多註解的代碼
*/
@WebService(name="myName",//對應portType name="myName"
portName="myPort", //對應服務中的port name="myPort"
serviceName="myService",//對應service name="myService"
targetNamespace="http://leaf.com/mynamespace")//可以隨意書寫類似於java中的package
public class HelloWorld{
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@WebMethod(action="myAction",//定義一個soapAction="myAction"用於找到這個方法以執行
operationName="myOperationName")//定義可以調用的方法,會生成相應類的具體方法,operation name=".."
public @WebResult(name="mySayHelloResult")String//定義返回值的名稱
sayHello(){
return "HelloWorld";
}
@WebMethod(action="mySayHiAction",operationName="mySayHiOperationName")
public @WebResult(name="mySayHiResult")String sayHi(@WebParam(name="myParaName",
//將參數放到頭信息中,用於保護參數,默認在body中
header=true,
mode=Mode.IN)
String name){
String str = "你好:"+name+",當前時間是:"+sdf.format(new Date());
return str;
}
public static void main(String[] args) {
Endpoint.publish("http://127.0.0.1:6666/helloworld",new HelloWorld());
}
}
3:將上面的程序對外發布以後,我們通過MyEclipse的WebService Explorer來訪問
你會發現和以前不一樣的提示信息,但其實,仍然還是調用的那同一個方法。
4:請同學們在去觀察SOAP請求和返回文檔的修改。在MyEclipse WebService Explorer的返回信息窗口中點Soure即可以看到。觀察變化加以分析。
5:再次使用wsimport –s . http://127.0.0.1:6666/helloworld?wsdl生成java代碼然後調用,看看還是哪些類名嗎?
以下是調用代碼(可以用面目全非來形容,但完成的還是同樣的工作。)
package com.leaf.mynamespace;
public class Main {
public static void main(String[] args) {
//通過分析wsdl可知從myService中調用getMyPort返回myName
MyName myName = new MyService().getMyPort();
//通過myName的mySayHiOperationName來調用sayHi方法
String str = myName.mySayHiOperationName("王健");
System.err.println(str);
}
}
package cn.leaf.two;
import java.util.Date;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
/**
* 發佈第一個web服務
*/
@WebService
(serviceName="WjService"//修改Service的名稱,即:new WjService();
,name="One"//定義Port名稱,即端口-new WjService().getOnePort();返回接口即One
,targetNamespace="http://wj.cn"//定義命名空間,默認爲倒置的包名
,portName="one"//定義獲取的方法,此值可以覆蓋name的定義,即port name="getOne"
)
public class OneService{
@WebMethod(operationName="sayHello"//修改方法名
)
public
@WebResult(name="ReturnMsg")
String sayHi(
@WebParam(name="yourName")
String name){
return name+",你好,現在時間是:"+new Date();
}
public static void main(String[] args) throws Exception {
//發佈服務
Endpoint ed= Endpoint.publish("http://127.0.0.1:9999/one",
new OneService());
System.err.println("服務發佈成功");
}
}