1 :WebService基础笔记 使用本机IP练习
无论你的服务端是什么语言书写的,只要接收SOAP协议的XML数据,并返回SOAP协议的XML数据,就可以被任何语言调用。
1.1 常用名词介绍:
l 名词1:XML.Extensible Markup Language -扩展性标记语言
• XML,用于传输格式化的数据,是Web服务的基础。
• namespace-命名空间。
• xmlns=“http://itcast.cn” 使用默认命名空间。
• xmlns:itcast=“http://itcast.cn”使用指定名称的命名空间。
•
l 名词2:WSDL –WebService Description Language – Web服务描述语言。
• 通过XML形式说明服务在什么地方-地址。
• 通过XML形式说明服务提供什么样的方法 – 如何调用。
•
l 名词3:SOAP-SimpleObject Access Protocol(简单对象访问协议)
• SOAP作为一个基于XML语言的协议用于有网上传输数据。
• SOAP = 在HTTP的基础上+XML数据。
• SOAP是基于HTTP的。
• SOAP的组成如下:
• Envelope – 必须的部分。以XML的根元素出现。
• Headers – 可选的。
• Body – 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据。
1.2 WSDL介绍
<?xml version="1.0" encoding="UTF8" ?> <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://ws.itcast.cn/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://ws.itcast.cn/" name="HelloServiceService"> <types> <xsd:schema> <xsd:import namespace="http://ws.itcast.cn/" schemaLocation="http://localhost:9999/hello?xsd=1" /> </xsd:schema> </types> <message name="sayHi"> <part name="parameters" element="tns:sayHi" /> </message> <message name="sayHiResponse"> <part name="parameters" element="tns:sayHiResponse" /> </message> <portType name="HelloService"> <operation name="sayHi"> <input message="tns:sayHi" /> <output message="tns:sayHiResponse" /> </operation> </portType> <binding name="HelloServicePortBinding" type="tns:HelloService"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> <operation name="sayHi"> <soap:operation soapAction="" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding> <service name="HelloServiceService"> <port name="HelloServicePort" binding="tns:HelloServicePortBinding"> <soap:address location="http://localhost:9999/hello" /> </port> </service> </definitions>
|
1.3 SOAP协议的范本:
1.3.1 请求示例:
POST /WebServices/MobileCodeWS.asmx HTTP/1.1 Host: webservice.webxml.com.cn Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://WebXml.com.cn/getMobileCodeInfo" <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <getMobileCodeInfo xmlns="http://WebXml.com.cn/"> <mobileCode>string</mobileCode> <userID>string</userID> </getMobileCodeInfo> </soap:Body> </soap:Envelope>
|
1.3.2 SOAP协议:-响应示例:
响应的信息,同发送信息一样,先必须是HTTP协议,然后再遵循SOAP协议
HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <getMobileCodeInfoResponse xmlns="http://WebXml.com.cn/"> <getMobileCodeInfoResult>string</getMobileCodeInfoResult> </getMobileCodeInfoResponse> </soap:Body> </soap:Envelope>
|
1.4 java项目中发布第一个WebService服务
在JDK1.6中JAX-WS规范定义了如何发布一个webService服务。
1: JAX-WS是指Java Api for XML –WebService.
用Jdk1.6.0_21以后的版本发布一个WebService服务.与Web服务相关的类,都位于javax.jws.*包中。主要类有:
@WebService - 它是一个注解,用在类上指定将此类发布成一个ws.Endpoint – 此类为端点服务类,它的方法publish用于将一个已经添加了@WebService注解对象绑定到一个地址的端口上。
1.4.1 本人实践——>发布服务使用wsimport:
注意:1:jdk的版本和IDE默认版本一致;
2:客服端说服务端同包;
命令参数说明:
-d:生成客户端执行类的class文件的存放目录
-s:生成客户端执行类的源文件的存放目录
-p:定义生成类的包名
1.4.1.11:写代码 启动服务
(服务端代码http://192.168.72.1:9000/Hello)
package cn.itcast.ws;
import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.xml.ws.Endpoint;
/** * WebService * 将 Java 类标记为实现 Web Service,或者将 Java 接口标记为定义 Web Service 接口 */ @WebService public class HelloService {
/* @WebMethod(operationName="abc") @WebResult(name="myReturn")(*/ public String sayHello(String name){ System.out.println("sayHello()..."); return "hello " + name; }
// exclude-->true:表示该方法不发布 @WebMethod(exclude=true) public String sayHello2(String name){ return "hello " + name; }
public static void main(String[] args) { /** * 参数1:服务的发布地址 * 参数2:服务的实现者 */ //本机IP"192.168.72.1" Endpoint.publish("http://192.168.72.1:9000/Hello", new HelloService()); System.out.println("Server ready..."); }
} |
1.4.1.2Dos中调用wsimport-s 生成java文件
2:win+R--->cmd---->d:回车;---〉输入:wsimport -s . http://192.168.72.1:9000/Hello?wsdl
d盘文件如下
问题:直接复制不是英文状态下输入的命令;
1.4.1.3客服端写代码
package cn.itcast.ws; /** * ͨ在wsimport客服端调用Webservice服务 */ publicclass App {
publicstaticvoid main(String[] args) { HelloServiceService hss = new HelloServiceService(); HelloService hs = hss.getHelloServicePort(); String ret = hs.sayHello("zhangsan"); System.out.println(ret); } } |
===end
1.4.2 如何发布一个web服务:
l 1、在类上添加@WebService注解。
• 这是jdk1.6提供的一个注解。它位于:javax.jws.*包中。
l 2、通过EndPoint(端点服务)发布一个webService。
• Endpoint也是jdk提供的一个专门用于发布服务的类,它的publish方法接收两个参数,一个是本地的服务地址,二是提供服务的类。它位于javax.xml.ws.*包中。
• static Endpoint.publish(String address,Object implementor)
在给定地址处针对指定的实现者对象创建并发布端点。
• stop方法用于停止服务。
• EndPoint发布完成服务以后,将会独立的线程运行。所以,publish之后的代码,可以正常执行。
l 其他注意事项:
• 给类添加上@WebService注解后,类中所有的非静态方法都将会对外公布。
• 不支持静态方法,final方法。-
• 如果希望某个方法(非static,非final)不对外公开,可以在方法上添加@WebMethod(exclude=true),阻止对外公开。
• 如果一个类上,被添加了@WebService注解,则必须此类至少有一个可以公开的方法,否则将会启动失败。
|
//1、添加注解
@WebService
public class OneService {
|
//2、至少包含一个可以对外公开的服务
publicString sayHello(String name){
System.err.println("invoke"+name);
return"Hello "+name;
|
}
publicstatic void main(String[] args) {
//3、第一个参数称为Binding即绑定地址,
//第二个参数是实现者,即谁提供服务
Endpoint.publish("http://localhost:9999/one",new OneService());
}
}
package com.itcast; import javax.jws.WebService; import javax.xml.ws.Endpoint; /** * 第一个WebService服务应用 */ //通过注解,标明此类发布为一个WebService @WebService publicclass HelloWorld { public String sayHello(){ return"Hello World"; } //在main方法中,使用javax.xml.ws.Endpoint端点发布一个应用 publicstaticvoid main(String[] args) { Endpoint.publish("http://127.0.0.1:9999/helloworld", new HelloWorld()); } }
|
l 在地址栏输入(注意后面的参数?wsdl)
• http://127.0.0.1:9999/helloworld?wsdl
l 目前不是访问webService,只是获取一个用于描述WebService的说明文件,即:wsdl文件.
wsdl- WebService Description Language,是以XML文件形式来描述WebService的”说明书”,有了说明书,我们才可以知道如何使用或是调用这个服务
1:在MyEclipse中新建立一个项目.在此项目中,将调用另一个项目中发布的WebService
2:步骤如下:
1:首先要根据http://127.0.0.1:9999/helloworld?wsdl获取WebService的使用说明书.
2:在Jdk的当前版本下,Jdk1.6.0_24,通过wsimport这个工具来生成远程调用的源代码.(建议生成扩展名为.java的文件)
3:在本项目中,通过调用生成代码的形式调用远程服务.成功返回”helloWorld”.
1.4.3 使用wsimport生成本地调用代码:
l 说明书看不懂怎么办?别急JDK能看懂:
l wsimport是jdk自带的,可以根据wsdl文档生成客户端调用代码的工具.当然,无论服务器端的WebService是用什么语言写的,都将在客户端生成Java代码.服务器端用什么写的并不重要.
l wsimport.exe位于JAVA_HOME\bin目录下.
l 常用参数为:
• -d<目录> - 将生成.class文件。默认参数。
• -s<目录> - 将生成.java文件。
• -p<生成的新包名> -将生成的类,放于指定的包下。
• (wsdlurl) - http://server:port/service?wsdl,必须的参数。
示例:
C:/>wsimport –s . http://192.168.0.100/one?wsdl
注意:-s不能分开,-s后面有个小点,用于指定源代码生成的目录。点即当前目录。
如果使用了-s参数则会在目录下生成两份代码,一份为.class代码。一份为.java代码。
.class代码,可以经过打包以后使用。.java代码可以直接Copy到我们的项目中运行。
1:可以通过java –version检查你当前的版本号.如果版本太低可以安装高版本的jdk.
或直接将别人已安装好的jdk目录拷贝到你的机器如D:\jdk1.6.0_24目录下.
因为以前的环境变量已经设置成以前老版本的jdk目录,即JAVA_HOME和PATH两个环境变量.
可以再重新设置一下环境变量为:JAVA_HOME=D:\jdk1.6.0_24,path=%JAVA_HOME%\bin,
重新设置了环境变量后,要重新打开一个doc(命令行)窗口.才生效.
如果不想修改原来已经配置好的环境变量,可以命令行窗口输入以下命令,使jdk1.6.0_24生效:
set path = D:\jdk1.6.0_24\bin;%PATH%(回车即可)
再通过java –version查看jdk的版本号是否已经发生变化.
2:转到一个相对干净的目录下,我在d盘上新建立一个目录名为:ws,并转到此目录下.
3:开启你的webService.
4:输入以下命令:
wsimport –s . http://127.0.0.1:9999/helloworld?wsdl
参数说明:-s是指编译出源代码文件,后面的.(点)指將代碼放到當前目錄下.
最后面的http….是指获取wsdl说明书的地址.
5:此时,将生成.java文件和.class文件.(都包含原始包名).将代码Copy到你的项目中.(只拷贝java文件)
6:在新的项目中,新一个类,(可位于任意包下),对上面生成的代码进行调用,见下一页ppt.
7:wsimport其他参数说明,我们经常使用的参数为-d,-s,-p
-d<目录>将会生成.class文件.
示例:wsimport –d . http://127.0.0.1:9999/helloworld?wsdl
-s<目录>将会生成.java文件.
示例:wsimport –s . http://127.0.0.1:9999/helloworld?wsdl
-p<包名>将生成的文件(.java或是.class修改成指定的包名)
示例:wsimport -s . -p com.beijing.itcasthttp://127.0.0.1:9999/helloworld?wsdl
对于-p参数,注意包名的修改,它将所生成类,全部置于通过-p指定的包下.(演示)
需要说明的是,当仅使用-p参数时,它也将同时使用-d即编译成.class文件. –d参数写或不写,它都在那里,不离不弃.
1.4.4 生成以后代码如下图所示:
RunMain.java的源代码如下: package com.leaf; import com.itcast.HelloWorld; import com.itcast.HelloWorldService; /** * 通过调用生成的类,来调用远程代码 */ publicclass RunMain { publicstaticvoid main(String[] args) { //从HelloWorldSerice的getHelloWorldPort方法中返回调用接口 HelloWorld helloWorld = new HelloWorldService().getHelloWorldPort(); String str = helloWorld.sayHello(); //执行调用 System.err.println(str);//返回HelloWorld字符串 } }
|
通过wsimport生成本地代码,调用网络上的web服务,比如手机号码归属地服务
1.5 WebService通过HTTP协议完成远程调用: (深入分析) –RPC
1.5.1 介绍
l WebService只采用HTTP POST方式传输数据,不使用GET方式; -- 握手,WSDL-get,
• 普通http post的contentType为
• application/x-www-form-urlencoded
• WebService的contentType为-即在Http的基础上发SOAP协议
• text/xml 这是基于soap1.1协议。
• application/soap+xml 这是基于soap1.2协议。
l WebService从数据传输格式上作了限定。WebService所使用的数据均是基于XML格式的。目前标准的WebService在数据格式上主要采用SOAP协议。SOAP协议实际上就是一种基于XML编码规范的文本协议。
l SOAP – Simple Object Access protocol 简单对像访问协议。是运行在HTTP协议基础之上的协议。其实就是在HTTP协议是传输XML文件,就变成了SOAP协议。
l SOAP1.1和SOAP1.2的namespace不一样。可以通过查看类
• javax.xml.ws.soap.SOAPBinding来查看里面的常量
• 默认情况下,Jdk1.6只支持soap1.1
• 即:@BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING)
1.5.2 WebService和Web服务器的区别
l WebService和Web服务器有什么区别呢?我们可以把WebService看作是Web服务器上应用;反过来说,Web服务器是WebService运行时所必需的容器。这就是它们的区别和联系。
l 使用JDK1.6发布的简单Web服务,其内部其实是使用Socket实现。可以查看:SUN公司未对外公布的API类com.sun.xml.internal.ws.transport.http.server.ServerMgr获知,请使用反编译工具。
l WebService的特点
• WebService通过HTTP POST方式接受客户的请求
• WebService与客户端之间一般使用SOAP协议传输XML数据.
• 它本身就是为了跨平台或跨语言而设计的。
1.6 --〉客户端调用WebService的方式<--
1.6.1 本人实践
Eclipse创建一个WebService浏览器服务:
|
l 通过客户端编程的方式调用
l 通过ajax调用js+XML
l 通过URLConnection调用
错误:thread "main"java.io.IOException: Server returned HTTP response code: 500 for URL:http://localhost:20000/hello
at s
URL url = new URL("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoInput(true); conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "text/xml;charset=UTF-8"); OutputStream out = conn.getOutputStream(); String soap = "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "<soap:Body><getMobileCodeInfo xmlns=\"http://WebXml.com.cn/\"><mobileCode>13436823445</mobileCode><userID></userID>" + "</getMobileCodeInfo></soap:Body></soap:Envelope>"; out.write(soap.getBytes()); int code = conn.getResponseCode(); if (code == 200) { InputStream is = conn.getInputStream(); byte[] b = newbyte[1024]; int len = 0; StringBuffer sb = new StringBuffer(); while ((len = is.read(b)) != -1) { String s = new String(b, 0, len, "UTF-8"); sb.append(s); } System.out.println(sb); } conn.disconnect();
|
1.6.2 使用原生的ajax调用web服务:
l 由于使用ajax – js调用web服务完成不同于使用java代码调用。所以,必须要对SOAP文件非常的了解。
l 一般使用ajax调用,应该是在已经获知了以下信息以后才去调用:
• 获知请求(request)的soap文本。
• 获知响应(response)的soap文本。
1.6.3 Ajax调用获取所有用户:
<html> <body> <button id="btn" οnclick="getUsers();">获取所有用户</button> </body> <script language="javascript"> var xhr = null; function getUsers(){ var url = "http://localhost:9999/user";//声明完整的url //以下声明soap文本 var soap = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" '+ 'xmlns:q0="http://ws2.itcast.cn/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" '+ 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'+ '<soapenv:Body><q0:users/></soapenv:Body></soapenv:Envelope>'; xhr.open("POST",url,true); xhr.onreadystatechange=back;//以下设置请求头 xhr.setRequestHeader("Content-Type","text/xml;charset=UTF-8"); xhr.send(soap);//发送xml请求 } function back(){ if(xhr.readyState==4){ if(xhr.status==200){ var xml = xhr.responseXML;//获取返回的xml文本并解析 var returns = xml.getElementsByTagName("return"); for(var i=0;i<returns.length;i++){ //var nm = returns[i].firstChild.text;//两种方式都可以 var nm = returns[i].getElementsByTagName("name")[0].text; alert(nm); } } } } //创建xhr对象,在IE上 function init(){ xhr = new ActiveXObject("Microsoft.XMLHTTP"); } init(); </script> </html>
|
1.6.4 客户端通过编程的方式访问服务:
l 使用javax.xml.ws.Service类用于访问web服务。
l 关键类Service
• 方法create – 用户创建Service对像,提供wsdlurl和服务名。
• getPort-用于通过指定namespace,portName和接口的范型。
• 在客户端需要一个与服务器接口完全相同的类。(仍然使用工具生成。但只需要一个接口。并需要简单修改。如果返回的是复杂数据类型如POJO,还需要将POJO一并放到项目中)-不要试图通过-p来修改包名,会出错的。
l 关键类QName – 被称为完全限定名即:QualifiedName的缩写。
• QName 的值包含名称空间URI、本地部分和前缀。
1、以下是调用代码(以下代码仅供参考) package com.itcast; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; import com.leaf.mynamespace.MyName; publicclass ClientTest { publicstaticvoid main(String[] args) throws Exception { //create的第一个参数为一个URL对像,接收wsdl的地址信息 //QName的参数:1: Service service = Service.create(new URL("http://localhost:6666/helloworld?wsdl"), new QName("http://leaf.com/mynamespace","myService")); System.err.println(service); //以下获取接口名 MyName o = service.getPort( new javax.xml.namespace.QName("http://leaf.com/mynamespace","myPort"), MyName.class); System.err.println(o.mySayHiOperationName("中国北京")); } } |
1.6.5 客户端编程-用的不是很多
package cn;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import cn.wj.One;
public class Test {
publicstatic void main(String[] args) throws Exception {
//声明wsdl地址
URLu = new URL("http://localhost:9999/one?wsdl");
//获取命名空间
Stringns = "http://wj.cn";
//第二个参数是一个完全限定名,WjService是服务名
Serviceservice = Service.create(u,new QName(ns,"WjService"));
//通过service的getPort方法返回指定的接口
Oneone = service.getPort(new QName(ns,"one"),One.class);
//调用sayHello方法
Stringss = one.sayHello("DDDD");
System.err.println(ss);
}
}
1.7 WebService监听工具:
l 之前我们使用过HttpWatch获取的HTTP的调用过程,并获得了HTTP的请求头及其他请求的详细信息。
l 既然WebServie也是通过HTTP进行通信的,能不使用HTTPWatch来获取它的请求过程呢?
l 我们的代码不仅仅是向服务器发送的HTTP协议,更具体的说应该叫SOAP协议,它是WebService进行通信的基础。
l 为了获取SOAP数据发送和接收的格式。我们有必要使用一个工具来深入的了解WebService.
l 为了监控拦截请求头和响应头的具体数据,我们使用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"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> </head> <body> <p>通过Ajax向服务器发送XML数据</p> <button οnclick="test1();">Ajax</button> </body> <script type="text/javascript"> var http; function test1(){ if(window.XMLHttpRequest){ http = new XMLHttpRequest(); }else{ http = new ActiveXObject("Microsoft.XMLHttp"); } var url = "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 var json = http.responseText; var person= eval("("+json+")"); alert("人员名单:"+person.name); } } }; http.send("<?xml version='1.0' encoding='utf-8'?>" + "<user><name>王健</name><name>张三</name></user>"); } </script> </html> --- 服务器使用jaxp进行解析 package cn.itcast; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** * 接收XML数据使用jaxp进行解析 * @author 王健 */ public class One extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取输入流,如果在输出,请在组成String时使用UTF-8 InputStream in = request.getInputStream(); String names = ""; // 使用JAXP解析 try { DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document dom = builder.parse(in); NodeList nl = dom.getElementsByTagName("name"); for(int i=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"); PrintWriter out = response.getWriter(); out.print("{\"name\":\""+names+"\"}"); } } -----以下通过jQuery+Dom4j实现发XML数据-------- <script type="text/javascript" src="js/jquery-1.6.2.js"></script> <script type="text/javascript"> $(function(){ $("#jq").click(function(){ var xml = "<?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> ---------------------------- package cn.itcast; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Iterator; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * JQuery+Dom4j * @author 王健 */ public class Two extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String result = ""; //中文new String(b,0,len,"UTF-8") InputStream in = request.getInputStream(); //以下用dom4j解析 SAXReader sax = 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"); PrintWriter out = response.getWriter(); //返回json数据 out.print("{\"name\":\""+result+"\"}"); } }
|
1.8 WS Explorer工具的使用:- web服务浏览器
使用此工具可以获取请求协议的格式和响应协议的格式。
在MyEclipse的调用WebService并查看发出和收到的数据格式!
1、将wsdl文件,保存成本地一样可以通过此工具访问远程服务。为file:///D:/abc.xml
输入正确的WSDL地址后,点确定,将显示所的对外暴露的方法:
输入正确的WSDL地址后,点确定,将显示所的对外暴露的方法:
在MyEclipse中调用WebService可以快速验证你的服务器端程序,从而省去了自己书写客户端。 1:这是发出的消息格式: <soapenv:Envelope xmlns: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:Envelope xmlns: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(Simple Object Access Protocol)简单对像访问协议的格式。
|
通过HTTP发出和接收到的XML数据:
|
1:我们已经说过了,WebService是通过向服务器发出XML格式的数据实现远程调用,然后服务器也返回XML数据给客户端,那么这个XML是什么格式的呢?
下面我将使用MyEclipse中的WebService Explorer工具向我们的WebService发起请求,并查看它的XML数据格式。
2:通过HttpWatchprofession Edition只可以看到获取wsdl文档的具体信息。
且必须安装HttpWatchprofession Edition版本的才可以,如果是Basic版本的,将不会看到Stream(数据流)信息。
请求的数据:
使用TCP/IP Monitor-拦截HTTP请求头和响应头及Body部分:
1.9 SOAP请求过程分析:
l 第一步:使用get方式获取wsdl文件,称为握手。
• 对于JDK1.6生成的ws服务,由于内部有一两个配置文件,所以会发出两次get请求。
• 其他的一般为一次。
l 第二步:用户发出请求将使用post方式。
l 第三步:服务器响应成功。
获取wsdl文件-握手的请求与响应:
以下是拦截到的请求/响应信息:
发出去的XML文本:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:q0="http://server.itcast.cn/"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body> //SOAP协议必须拥有body元素
<q0:sayHello> //SOAP协议必须通过第一个节点指明需要调用的方法
<arg0>aaa</arg0>
</q0:sayHello>
</soapenv:Body>
</soapenv:Envelope>
拦截到的返回信息:
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:sayHiResponsexmlns:ns2="http://ws.itcast.cn/">
<return>HelloWorld</return>
</ns2:sayHiResponse>
</S:Body>
</S:Envelope>
深入分析说明书WSDL:
l wsdl – WebService Description Language(WS描述语言)
l 它主要定义了三个方面的问题:
• What?即服务是什么?
• (portType,types,message)
• How?如何调用服务?
• 通过binding元素说明调用服务的方式:soap,soap12,post,get.
• Where?在哪儿调用服务?
• Service元素,soap:address.
|
修改wsdl文件的内容:
l WSDL文件的内容,一般由服务默认生成,但为了更好的向开发人员提供使用说明书,一般应做一些简单的修改。至少不应该暴露我们的包结构。而targetNamespace默认情况下为倒置的包名,这已经暴露了我们的包结构。
l 通过在类文件上添加以下注解,可以修改wsdl生成的各元素,而不是直接去修改wsdl文件,直接去修改wsdl文件是无效的。
l WebService的注解包括:
• @WebService-定义服务 --类上
• @WebMethod-定义方法 - 方法
• @WebResult-定义返回值– 返回值
• @WebParam-定义参数– 参数
•
l 1、另有:SOAPBinding-指定WebService到SOAP协议的影射关系?
l 使用不同版本的Jdk对发布ws的影响.
l 1.5不支持.
l 1.6.0_20前版本必须使用完整注解.
l 1.6.0_21以后可以只使用@WebService对类进行注解.
l 2、关于namespace约定名的说明,@WebService(targetNameSpace=…..)
l targetNamespace
l 定义导出的服务接口的名域(namespace),默认是倒置的服务接口Java包名。如demo.cxf.UserService的名域将会是http://cxf.demo/
注解的作用
l 通过WebService的注解,可以更加形像的描述Web服务。从而生成WSDL文档。
l 当修改了WebService注解之后,同时会影响客户端生成的代码。
l 调用的方法名和参数名也发生了变化。
l 即使是没有修改源代码,只修改了注解,客户端的代码也必须要重新生成(注意是生成而不是下载)。否则调用将会失败。
l 生成本地调用代码,依然使用wsimport工具。
1:WebService的注解都位于javax.jws包下。 主要包含以下几个注解(直接查看JDK文档,关于它里面的配置属性也直接看JDK6的文档。) 我们只讨论以下加个注解: WebMethod 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 publicclass HelloWorld{ privatestatic 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; } publicstaticvoid 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; publicclass Main { publicstaticvoid main(String[] args) { //通过分析wsdl可知从myService中调用getMyPort返回myName MyName myName = new MyService().getMyPort(); //通过myName的mySayHiOperationName来调用sayHi方法 String str = myName.mySayHiOperationName("王健"); System.err.println(str); } }
|
@WebService注解:
l @WebService注解,作用在具体类上。而不是接口。
l 一个类只有添加了此注解才可以通过Endpoint发布为一个web服务。
l 一个添加了此注解的类,必须要至少包含一个实例方法。静态方法和final方法不能
l 被发布为服务方法。
l WebService注解包含以下参数:
@WebMethod
@WebResult用于定制返回值到WSDL的映射:
@WebParam用于定义WSDL中的参数映射:
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("服务发布成功"); } }
|
从wsdl中分析出类的关系: