Web Service 运行原理详细介绍
利用清明小假期,温习了一遍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配置
<!-- 在向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):
<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服务
/** * 通过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):
<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数据后会进行相应的解析操作。如下:
// 将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服务的基本工作原理。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!