webservice原理详解_webservice客户端调用

webservice原理详解_webservice客户端调用WebService和编程语言中的类相似,它们都实现了一些功能,并且通过方法供给外部调用,不同的是WebService是部署在网络服务器上,而类在同一个进程空间里面。WebService部署在网络上,通过网络进行服务调用,只要遵循了访问协议,任何编程语言都可以访问服务,所以它是编程语言不相关的。我们可以通过类文件知道一个类提供了哪些方法,那怎么知道WebService提供了哪些服务供外部调用呢...

WebService和编程语言中的类相似,它们都实现了一些功能,并且通过方法供给外部调用,不同的是WebService是部署在网络服务器上,而类在同一个进程空间里面。WebService部署在网络上,通过网络进行服务调用,只要遵循了访问协议,任何编程语言都可以访问服务,所以它是编程语言不相关的。

我们可以通过类文件知道一个类提供了哪些方法,那怎么知道WebService提供了哪些服务供外部调用呢?每一个WebService都有一个WSDL文件,WSDL全称Web Services Description Language(网络服务描述语言),WSDL有两个作用:

  1. 描述了WebService提供的服务,就像类里面的方法,包含名字、参数、返回值。
  2. 描述了访问服务的方法。

下面是一个WSDL的示例文件:

 
<?xml version="1.0" encoding="utf-8"?>

<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.7-b01  svn-revision#${svn.Last.Changed.Rev}. -->
<!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.7-b01  svn-revision#${svn.Last.Changed.Rev}. -->
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://example/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example/" name="HelloWorldService">  
  <types> 
    <xsd:schema> 
      <xsd:import namespace="http://example/" schemaLocation="http://localhost:9090/HelloWorld?xsd=1"/> 
    </xsd:schema> 
  </types>  
  <message name="sayHelloWorld"> 
    <part name="parameters" element="tns:sayHelloWorld"/> 
  </message>  
  <message name="sayHelloWorldResponse"> 
    <part name="parameters" element="tns:sayHelloWorldResponse"/> 
  </message>  
  <portType name="HelloWorld"> 
    <operation name="sayHelloWorld"> 
      <input wsam:Action="http://example/HelloWorld/sayHelloWorldRequest" message="tns:sayHelloWorld"/>  
      <output wsam:Action="http://example/HelloWorld/sayHelloWorldResponse" message="tns:sayHelloWorldResponse"/> 
    </operation> 
  </portType>  
  <binding name="HelloWorldPortBinding" type="tns:HelloWorld"> 
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>  
    <operation name="sayHelloWorld"> 
      <soap:operation soapAction=""/>  
      <input> 
        <soap:body use="literal"/> 
      </input>  
      <output> 
        <soap:body use="literal"/> 
      </output> 
    </operation> 
  </binding>  
  <service name="HelloWorldService"> 
    <port name="HelloWorldPort" binding="tns:HelloWorldPortBinding"> 
      <soap:address location="http://localhost:9090/HelloWorld"/> 
    </port> 
  </service> 
</definitions>

 
只听到从架构师办公室传来架构君的声音:
湛湛长空黑。有谁来对上联或下联?

WSDL包含了五部分内容:

  • types
  • message
  • portType
  • binding
  • service

其中portType里面的内容描述了WebService提供的服务,上例中的portType内容可以翻译为Java类,如下:

此代码由Java架构师必看网-架构君整理
<portType name="HelloWorld"> ==> 对应类名 <operation name="sayHelloWorld"> ==> 对应方法名 ...... </operation> </portType>
//翻译后的portType内容
public class HelloWorld {
  public ... sayHelloWorld(...) {
          // ......
  }
}

 

可以看到sayHelloWorld方法中的参数和返回值使用了"...",这是因为通过portType无法直接得到参数和返回值,所以用“...”代替。至于具体的参数和返回值怎么获取,我们在后面讲解,姑且先当做void看待。现在知道了WebService提供的方法,要怎么调用它呢?还是得看WSDL,我们知道WebService是部署在网络上的,访问WebService就需要进行网络数据交互,网络数据交互需要通信协议。 WSDL中的binding部分描述了通信协议,WebService通常是HTTP + SOAP(Simple Object Access Protocol 简单对象访问协议)协议来进行数据交互,SOAP基于XML它被设计成在WEB上交换结构化的和固化的信息。本例中WebService也是采用HTTP+SOAP协议进行数据交互,本文中WSDL的binding内容如下所示:

此代码由Java架构师必看网-架构君整理
<!-- 为HelloWorld绑定通信协议 --> <binding name="HelloWorldPortBinding" type="tns:HelloWorld"> <!-- HelloWorld 使用 http + soap 协议进行数据交互--> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="sayHelloWorld"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding>

下面是一个 HTTP+SOAP 示例:

POST URI HTTP/1.1
Accept: text/xml, multipart/related
Content-Type: text/xml; charset=utf-8
SOAPAction: "Action"
User-Agent: JAX-WS RI 2.2.7-b01  svn-revision#${svn.Last.Changed.Rev}
Host: localhost:9090
Connection: keep-alive
Content-Length: xxx

<!-- SOAP协议 -->
<?xml version="1.0" encoding="utf-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">     
    <S:Body>
        <!-- 内容由使用者定义 -->
        <ns2:自定义元素 xmlns:ns2="http://example/">
            自定义元素内容
        </ns2:自定义元素>
    </S:Body>
</S:Envelope>

可以看到HTTP+SOAP就是把SOAP放入HTTP的body部分使用POST发送给WebService服务器。SOAP协议的Body内容由使用者定义,WSDL为每个WebService方法的调用和返回定义了SOAP body格式也就是定义了一个XML元素,只要找到 WebService 方法对应的自定义元素,就能封装SOAP协议了。通过WSDL的portType 、message两部分内容可以找到方法对应的自定义元素,本例中这两部分内容如下所示:

<message name="sayHelloWorld"> 
    <!-- 关联了一个sayHelloWorld元素 -->
    <part name="parameters" element="tns:sayHelloWorld"/> 
</message>  
<message name="sayHelloWorldResponse"> 
    <!-- 关联了一个sayHelloWorldResponse元素 -->
    <part name="parameters" element="tns:sayHelloWorldResponse"/> 
</message>    
 
<portType name="HelloWorld"> 
    <operation name="sayHelloWorld"> 
      <!-- input关联了一个sayHelloWorld message-->
      <input wsam:Action="http://example/HelloWorld/sayHelloWorldRequest" message="tns:sayHelloWorld"/>  
      <!-- output关联了一个sayHelloWorldResponse message-->
      <output wsam:Action="http://example/HelloWorld/sayHelloWorldResponse" message="tns:sayHelloWorldResponse"/> 
    </operation> 
</portType>  

portType中的sayHelloWorld方法有一个input和output,input表示调用,output表示返回,input元素关联了一个message,本例中为“sayHelloWorld”,这个message关联了一个元素,本例中为sayHelloWorld,message关联的这个元素就是我们用来填充到SOAP body的元素了,同理output关联的元素为 “ sayHelloWorldResponse ” 。现在我们知道了调用 sayHelloWorld方法时使用
sayHelloWorld 元素填充Soap body,返回值使用 sayHelloWorldResponse 元素填充Soap body。但是我们还不知道这两个元素的具体定义,通过types部分我们可以知道这两个元素的具体定义,本例中的types定义如下:

 <types> 
    <xsd:schema> 
      <!--从http://localhost:9090/HelloWorld?xsd=1引入元素定义 -->
      <xsd:import namespace="http://example/" schemaLocation="http://localhost:9090/HelloWorld?xsd=1"/> 
    </xsd:schema> 
 </types>  

types从http://localhost:9090/HelloWorld?xsd=1引入了元素定义,引入的内容如下:

 <?xml version="1.0" encoding="utf-8"?>
 
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://example/" version="1.0" targetNamespace="http://example/">  
   <xs:element name="sayHelloWorld" type="tns:sayHelloWorld"/>  
   <xs:element name="sayHelloWorldResponse" type="tns:sayHelloWorldResponse"/>  
   <xs:complexType name="sayHelloWorld"> 
     <xs:sequence> 
       <!--  sayHelloWorld方法的参数 -->
       <xs:element name="arg0" type="xs:string" minOccurs="0"/> 
     </xs:sequence> 
   </xs:complexType>  
   <xs:complexType name="sayHelloWorldResponse"> 
     <xs:sequence> 
       <!--   sayHelloWorld方法的返回值  -->
       <xs:element name="return" type="xs:string" minOccurs="0"/> 
     </xs:sequence> 
   </xs:complexType> 
 </xs:schema>   

可以看到sayHelloWorld元素包含一个类型为string的arg0元素,表示 sayHelloWorld 方法有一个string类型的参数。saysayHelloWorldResponse包含一个类型为string的return元素, 表示 sayHelloWorld 返回一个string类型的值。知道了参数和方法Webservice描述的方法就可以用Java完整的翻译出来,如下:

//WebService提供的服务
public class HelloWorld {
  public String sayHelloWorld(String arg0) {
          // ......
  }
}

知道了 sayHelloWorld 元素和 saysayHelloWorldResponse 元素的定义以后我们就知道怎么封装Soap了,调用 sayHelloWorld 并且传参数jack的Soap封装为:

 <?xml version="1.0" encoding="utf-8"?>
 <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
     <!-- 元素sayHelloWorld包含一个string类型的arg0元素表示参数  -->
     <ns2:sayHelloWorld xmlns:ns2="http://example/">
       <!-- 传参jack -->
       <arg0>jack</arg0>
     </ns2:sayHelloWorld>
   </S:Body>
 </S:Envelope> 

返回时的Soap封装如下:

 <?xml version="1.0" encoding="utf-8"?>
 <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body> 
     <!-- 元素 sayHelloWorldResponse 包含一个string类型的return元素表示返回值  --> 
     <ns2:sayHelloWorldResponse xmlns:ns2="http://example/">
       <return>Hello, world, from jack</return>
     </ns2:sayHelloWorldResponse>
   </S:Body>
 </S:Envelope> 

可以看到sayHelloWorld返回值是“Hello, world, from jack”。封装好Soap协议以后就可以往Webservice服务器发送协议了,但是我们目前还不知道Webservice的地址,通过WSDL的service部分可以获取Webservice的地址,本例中的service内容如下:

 <!-- WebService的地址从service部分获取 -->   
<service name="HelloWorldService"> 
    <port name="HelloWorldPort" binding="tns:HelloWorldPortBinding"> 
      <!--  HelloWorld 的URI -->
      <soap:address location="http://localhost:9090/HelloWorld"/> 
    </port> 
</service>   

地址http://localhost:9090/HelloWorld即为Webservice的服务器地址。另外HTTP头部有
SOAPAction需要填充,它来自 WSDL的portType的input和output,示例如下:

<input wsam:Action="http://example/HelloWorld/sayHelloWorldRequest" message="tns:sayHelloWorld"/>        
<output wsam:Action="http://example/HelloWorld/sayHelloWorldResponse" message="tns:sayHelloWorldResponse"/>  

wsam:Action内容即是HTTP头部的SOAPAction。至此我没可以组装出完整的HTTP+SOAP,完整的调用协议内容如下:

 POST  /HelloWorld  HTTP/1.1
 Accept: text/xml, multipart/related
 Content-Type: text/xml; charset=utf-8
 SOAPAction: "http://example/HelloWorld/sayHelloWorldRequest"
 User-Agent: JAX-WS RI 2.2.7-b01  svn-revision#${svn.Last.Changed.Rev}
 Host: localhost:9090
 Connection: keep-alive
 Content-Length: xxx  


 <?xml version="1.0" encoding="utf-8"?>
 <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
     <!-- 元素sayHelloWorld包含一个string类型的arg0元素表示参数  -->
     <ns2:sayHelloWorld xmlns:ns2="http://example/">
       <!-- 传参jack -->
       <arg0>jack</arg0>
     </ns2:sayHelloWorld>
   </S:Body>
 </S:Envelope>  

完整的返回协议如下:

 HTTP/1.1 200 OK
 Date: Fri, 17 May 2019 08:34:13 GMT
 Transfer-encoding: chunked
 Content-type: text/xml; charset=utf-8


 <?xml version="1.0" encoding="utf-8"?>
 <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body> 
     <!-- 元素 sayHelloWorldResponse 包含一个string类型的return元素表示返回值  --> 
     <ns2:sayHelloWorldResponse xmlns:ns2="http://example/">
       <return>Hello, world, from jack</return>
     </ns2:sayHelloWorldResponse>
   </S:Body>
 </S:Envelope> 

虽然WebService的底层很复杂,但是大多数编程语言都有现成的框架帮助我们完成底层的细节,具体的我们在编写WebService时通常只用创建一个类并且实现类的一些方法, 框架会根据类自动生成WSDL 。WebService的调用也很简单,框架可以根据WSDL生成一个类,我们只需实例化对象然后调用方法获取返回值,和使用普通的类没什么两样。

WSDL是Webservice中最重要的内容,理解WSDL对使用Webservice和调试Webservice非常有用,
它包含五部分内容,通过上文的学习我们可以总结出每个部分的功能:

  • service,提供了WebService的访问地址 。
  • portType,定义了WebService提供的服务。
  • binding,定义了WebService通信协议 。
  • message,定义了每个服务调用的和返回的格式,常通过引用一个自定义元素,由自定义的元素 (element) 描述格式。
  • types,定义了message引用的各种自定义元素(element)。

本文描述的WSDL使用Document literal wapped 格式的协议,它还支持RPC literal/encode 协议,关于两种协议的区别和各自的应用场景会在后面的文章中讲解。

架构君码字不易,如需转载,请注明出处:https://javajgs.com/archives/222234
0
 

发表评论