利用清明小假期,温习了一遍Web Service的相关内容,对其工作原理进行了简要总结。以供有需求的朋友和自己日后参考。文章若有不当之处,敬请朋友们提出宝贵建议,以求共勉。
Web服务中,我们应该首先了解相关的术语含义:WSDL、UDDI....相关术语方面的介绍在此不再赘述,重点放在原理上。
在Web服务中,存在三个角色:服务提供者、服务请求者和服务中介,三者之间的关系如图1-1所示
实现一个完整的Web服务包括以下步骤:
◆ Web服务提供者设计实现Web服务,并将调试正确后的Web服务通过Web服务中介者发布,并在UDDI注册中心注册; (发布)
◆ Web服务请求者向Web服务中介者请求特定的服务,中介者根据请求查询UDDI注册中心,为请求者寻找满足请求的服务; (发现)
◆ Web服务中介者向Web服务请求者返回满足条件的Web服务描述信息,该描述信息用WSDL写成,各种支持Web服务的机器都能阅读;(发现)
◆ 利用从Web服务中介者返回的描述信息(WSDL)生成相应的SOAP消息,发送给Web服务提供者,以实现Web服务的调用;(绑定)
◆ Web服务提供者按SOAP消息执行相应的Web服务,并将服务结果返回给Web服务请求者。(绑定)
图1-1 Web service的体系结构
注:WSDL的作用就是一个Web服务说明书。服务请求者根据此WSDL生成相应的SOAP消息,服务提供者在收到SOAP请求消息后,进行服务的绑定。
以下代码是在web.xml中的servlet配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<!-- 在向servlet或JSP页面制定初始化参数或定制URL时,必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。 --> < servlet > < servlet-name >UserService</ servlet-name > < servlet-class >com.sun.xml.ws.transport.http.servlet.WSServlet</ servlet-class > <!-- 标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法;正数的值越小,该servlet的优先级越高,应用启动时就越先加载 --> < load-on-startup >1</ load-on-startup > </ servlet > <!-- 服务器一般为servlet提供一个缺省的URL:http://host/webAppPrefix/servlet/ServletName。 但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时,使用servlet-mapping元素。 --> < servlet-mapping > < servlet-name >UserService</ servlet-name > <!-- 描述了相对于Web应用的根目录的URL。url-pattern元素的值必须以斜杠(/)起始。 --> < url-pattern >/user</ url-pattern > </ servlet-mapping > 红色代码部分很重要,会在Web容器启动的时候加载相应的servlet。绿色部分为该服务的外部接口。以此找到相应的jax-ws.xml文件(如下所示) < endpoint name = "UserPort" implementation = "cn.ujn.service.UserService" url-pattern = "/user" > </ endpoint > |
进而绑定到相关的相应的实现类cn.ujn.service.UserService中。客户端发送的SOAP请求消息消息体body中包含有客户端所请求的方法名和参数信息。
以下为客户端封装的soap消息体(以Json方式与服务端进行数据传输)(SOAP Rerquest Envelope):
1
2
3
4
5
6
7
|
<soapenv:Envelope xmlns:soapenv= "http://schemas.xmlsoap.org/soap/envelope/" xmlns:q0= "http://ujn.cn/" xmlns:xsd= "http://www.w3.org/2001/XMLSchema" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" > - <soapenv:Body> - <q0:login> <arg0>{ "username" : "shq" , "password" : "shq" }</arg0> </q0:login> </soapenv:Body> </soapenv:Envelope> |
以下为SOAP1.1协议调用Web服务
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
/** * 通过SOAP1.1协议调用Web服务 * * text/xml 这是基于soap1.1协议 * * @param wsdl WSDL路径 * @param method方法名 * @param namespace命名空间 * @param headerParameters 头参数 * @param bodyParameters 体参数 * @param isBodyParametersNS 体参数是否有命名空间 * @return String * @throws Exception */ public static String invokeBySoap11(String wsdl, String method, String namespace, Map<String, String> headerParameters, Map<String, String> bodyParameters, boolean isBodyParametersNS) throws Exception { StringBuffer soapOfResult = null ; // 去除 ?wsdl,获取方法列表 int length = wsdl.length(); wsdl = wsdl.substring( 0 , length - 5 ); //以字符串为参数创建URL实例 URL url = new URL(wsdl); //创建连接 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //设置请求方式 conn.setRequestMethod( "POST" ); //如果打算使用 URL连接进行输入,则将 DoInput 标志设置为 true conn.setDoInput( true ); //如果打算使用 URL连接进行输出,则将 DoInput 标志设置为 true conn.setDoOutput( true ); //主要是设置HttpURLConnection请求头里面的属性(K-V) conn.setRequestProperty( "Content-Type" , "text/xml;charset=utf-8" ); //获取输入流(相对于客户端来说,使用的是OutputStream) OutputStream out = conn.getOutputStream(); // 获取soap1.1版本消息 StringBuilder sb = new StringBuilder(); sb.append("<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/\" " ); sb.append( "xmlns:ns0=\"" + namespace + "\"" ); sb.append( ">" ); //拼装消息头 if (headerParameters != null ) { sb.append( "<soap:Header>" ); for (Entry<String, String> headerParameter : headerParameters .entrySet()) { sb.append( "<ns0:" ); sb.append(headerParameter.getKey()); sb.append( ">" ); sb.append(headerParameter.getValue()); sb.append( "</ns0:" ); sb.append(headerParameter.getKey()); sb.append( ">" ); } sb.append( "</soap:Header>" ); } //拼装消息体 sb.append( "<soap:Body><ns0:" ); sb.append(method); sb.append( ">" ); // 输入参数 if (bodyParameters != null ) { for (Entry<String, String> inputParameter : bodyParameters .entrySet()) { if (isBodyParametersNS) { sb.append( "<ns0:" ); sb.append(inputParameter.getKey()); sb.append( ">" ); sb.append(inputParameter.getValue()); sb.append( "</ns0:" ); sb.append(inputParameter.getKey()); sb.append( ">" ); } else { sb.append( "<" ); sb.append(inputParameter.getKey()); sb.append( ">" ); sb.append(inputParameter.getValue()); sb.append( "</" ); sb.append(inputParameter.getKey()); sb.append( ">" ); } } } sb.append( "</ns0:" ); sb.append(method); sb.append( "></soap:Body></soap:Envelope>" ); //测试用 System.out.println(sb.toString()); //写入SOAP消息(相对于客户端来说,使用的是out.write()) out.write(sb.toString().getBytes()); //获取服务器端的相应 int code = conn.getResponseCode(); if (code == 200 ) { InputStream is = conn.getInputStream(); byte [] b = new byte [ 1024 ]; int len = 0 ; soapOfResult = new StringBuffer(); //从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数 //如果因为流位于文件末尾而没有可用的字节,则返回值 -1; while ((len = is.read(b)) != - 1 ) { //Converts the byte array to a string using the named charset. String s = new String(b, 0 , len, "UTF-8" ); soapOfResult.append(s); } } conn.disconnect(); return soapOfResult == null ? null : soapOfResult.toString(); } |
注:在客户端发送SOAP请求消息后便处于阻塞状态。直至服务端返回状态码。
以下为服务端进行响应(SOAP Response Envelope):
1
2
3
4
5
6
7
|
< S:Envelope xmlns:S = "http://schemas.xmlsoap.org/soap/envelope/" > -< S:Body > -< ns2:loginResponse xmlns:ns2 = "http://ujn.cn/" > < return >1</ return > </ ns2:loginResponse > </ S:Body > </ S:Envelope > |
客户端接收到服务端发来的Json数据后会进行相应的解析操作。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// 将Soap协议进行解析(DOM解析只能用于解析XML文档类型,而SOAP消息就是采用XML数据格式) Document doc = XmlUtil.string2Doc(result); Element ele = (Element) doc.getElementsByTagName( "return" ).item( 0 ); 方法中使用到的string2Doc()方法体如下: public static Document string2Doc(String str) { //将XML文档解析成DOM树 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); Document document = null ; DocumentBuilder build; if (str == null || str.equals( "" )) { return null ; } try { InputStream bais = new ByteArrayInputStream(str.getBytes( "UTF-8" )); build = factory.newDocumentBuilder(); //Parse the content of the given InputStream as an XML document and return a new DOM Document object. document = build.parse(bais); } catch (Exception e) { e.printStackTrace(); } return document; } |
根据返回结果,客户端再进行相应的处理。
以上是web服务的基本工作原理。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!