写点什么

Web Service 进阶 (一) 运行原理

  • 2022 年 6 月 05 日
  • 本文字数:3506 字

    阅读完需:约 12 分钟

Web Service进阶(一)运行原理

Web 服务中,我们应该首先了解相关的术语含义:WSDLUDDI....相关术语方面的介绍在此不再赘述,重点放在原理上。


在 Web 服务中,存在三个角色:服务提供者、服务请求者和服务中介,三者之间的关系如图 1-1 所示。


实现一个完整的 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 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;}
复制代码

根据返回结果,客户端再进行相应的处理。

发布于: 2022 年 06 月 05 日阅读数: 6
用户头像

No Silver Bullet 2021.07.09 加入

岂曰无衣 与子同袍

评论

发布
暂无评论
Web Service进阶(一)运行原理_6月月更_No Silver Bullet_InfoQ写作社区