SOAP, WSDL, Web Service, WCF

在一个新工作里,总要学习点什么,这个企业里使用的ERP系统是SAP Business one,使用的系统集成中间件平台是SAP B1iSN,在B1iSN里主要使用的ETL技术是XSL。所以这两天把XML有关的知识恶补了一下,XML、XSLT、XPath、XQuery、XSD、WSDL、SOAP等都简单地看了一下,今天在这里要讲的并不是XML,而是WCF和Web Service,但它们都是基于XML的。

WCF是微软多年前发明的一个SOA技术,虽然讲现在大家更推崇的是RestAPI(WebAPI),但是不可否认的是作为一个成熟的分布式SOA技术,WCF仍然有着很强的生命力,并且微软也在紧锣密鼓地开发DotNet Core版本的WCF,也就是讲未来WCF是可以跨平台运行的。那我们现在来看看WCF是怎么做到提供SOA服务的。

  1. 首先你如果要使用别人的服务,那么你得知道别人提供什么服务,反过来讲就是我得告诉你我有什么服务,那么在这点上WCF是通过WSDL将服务进行描述,当你打开我提供出来的WSDL,那么你就能够根据描述知道我提供了什么。下面就是一个标准的由WCF框架生成的WSDL文件:
    <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://tempuri.org/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" name="Foobar" targetNamespace="http://tempuri.org/">
    <wsdl:types>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
    <xs:import namespace="http://schemas.datacontract.org/2004/07/Wcf"/>
    <xs:element name="GetData">
    <xs:complexType>
    <xs:sequence>
    <xs:element minOccurs="0" name="value" type="xs:int"/>
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    <xs:element name="GetDataResponse">
    <xs:complexType>
    <xs:sequence>
    <xs:element minOccurs="0" name="GetDataResult" nillable="true" type="xs:string"/>
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    <xs:element name="GetDataUsingDataContract">
    <xs:complexType>
    <xs:sequence>
    <xs:element xmlns:q1="http://schemas.datacontract.org/2004/07/Wcf" minOccurs="0" name="composite" nillable="true" type="q1:CompositeType"/>
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    <xs:element name="GetDataUsingDataContractResponse">
    <xs:complexType>
    <xs:sequence>
    <xs:element xmlns:q2="http://schemas.datacontract.org/2004/07/Wcf" minOccurs="0" name="GetDataUsingDataContractResult" nillable="true" type="q2:CompositeType"/>
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    </xs:schema>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/">
    <xs:element name="anyType" nillable="true" type="xs:anyType"/>
    <xs:element name="anyURI" nillable="true" type="xs:anyURI"/>
    <xs:element name="base64Binary" nillable="true" type="xs:base64Binary"/>
    <xs:element name="boolean" nillable="true" type="xs:boolean"/>
    <xs:element name="byte" nillable="true" type="xs:byte"/>
    <xs:element name="dateTime" nillable="true" type="xs:dateTime"/>
    <xs:element name="decimal" nillable="true" type="xs:decimal"/>
    <xs:element name="double" nillable="true" type="xs:double"/>
    <xs:element name="float" nillable="true" type="xs:float"/>
    <xs:element name="int" nillable="true" type="xs:int"/>
    <xs:element name="long" nillable="true" type="xs:long"/>
    <xs:element name="QName" nillable="true" type="xs:QName"/>
    <xs:element name="short" nillable="true" type="xs:short"/>
    <xs:element name="string" nillable="true" type="xs:string"/>
    <xs:element name="unsignedByte" nillable="true" type="xs:unsignedByte"/>
    <xs:element name="unsignedInt" nillable="true" type="xs:unsignedInt"/>
    <xs:element name="unsignedLong" nillable="true" type="xs:unsignedLong"/>
    <xs:element name="unsignedShort" nillable="true" type="xs:unsignedShort"/>
    <xs:element name="char" nillable="true" type="tns:char"/>
    <xs:simpleType name="char">
    <xs:restriction base="xs:int"/>
    </xs:simpleType>
    <xs:element name="duration" nillable="true" type="tns:duration"/>
    <xs:simpleType name="duration">
    <xs:restriction base="xs:duration">
    <xs:pattern value="\-?P(\d*D)?(T(\d*H)?(\d*M)?(\d*(\.\d*)?S)?)?"/>
    <xs:minInclusive value="-P10675199DT2H48M5.4775808S"/>
    <xs:maxInclusive value="P10675199DT2H48M5.4775807S"/>
    </xs:restriction>
    </xs:simpleType>
    <xs:element name="guid" nillable="true" type="tns:guid"/>
    <xs:simpleType name="guid">
    <xs:restriction base="xs:string">
    <xs:pattern value="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}"/>
    </xs:restriction>
    </xs:simpleType>
    <xs:attribute name="FactoryType" type="xs:QName"/>
    <xs:attribute name="Id" type="xs:ID"/>
    <xs:attribute name="Ref" type="xs:IDREF"/>
    </xs:schema>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.datacontract.org/2004/07/Wcf" elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/Wcf">
    <xs:complexType name="CompositeType">
    <xs:sequence>
    <xs:element minOccurs="0" name="BoolValue" type="xs:boolean"/>
    <xs:element minOccurs="0" name="StringValue" nillable="true" type="xs:string"/>
    </xs:sequence>
    </xs:complexType>
    <xs:element name="CompositeType" nillable="true" type="tns:CompositeType"/>
    </xs:schema>
    </wsdl:types>
    <wsdl:message name="IFoobar_GetData_InputMessage">
    <wsdl:part name="parameters" element="tns:GetData"/>
    </wsdl:message>
    <wsdl:message name="IFoobar_GetData_OutputMessage">
    <wsdl:part name="parameters" element="tns:GetDataResponse"/>
    </wsdl:message>
    <wsdl:message name="IFoobar_GetDataUsingDataContract_InputMessage">
    <wsdl:part name="parameters" element="tns:GetDataUsingDataContract"/>
    </wsdl:message>
    <wsdl:message name="IFoobar_GetDataUsingDataContract_OutputMessage">
    <wsdl:part name="parameters" element="tns:GetDataUsingDataContractResponse"/>
    </wsdl:message>
    <wsdl:portType name="IFoobar">
    <wsdl:operation name="GetData">
    <wsdl:input wsaw:Action="http://tempuri.org/IFoobar/GetData" message="tns:IFoobar_GetData_InputMessage"/>
    <wsdl:output wsaw:Action="http://tempuri.org/IFoobar/GetDataResponse" message="tns:IFoobar_GetData_OutputMessage"/>
    </wsdl:operation>
    <wsdl:operation name="GetDataUsingDataContract">
    <wsdl:input wsaw:Action="http://tempuri.org/IFoobar/GetDataUsingDataContract" message="tns:IFoobar_GetDataUsingDataContract_InputMessage"/>
    <wsdl:output wsaw:Action="http://tempuri.org/IFoobar/GetDataUsingDataContractResponse" message="tns:IFoobar_GetDataUsingDataContract_OutputMessage"/>
    </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="BasicHttpBinding_IFoobar" type="tns:IFoobar">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="GetData">
    <soap:operation soapAction="http://tempuri.org/IFoobar/GetData" style="document"/>
    <wsdl:input>
    <soap:body use="literal"/>
    </wsdl:input>
    <wsdl:output>
    <soap:body use="literal"/>
    </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="GetDataUsingDataContract">
    <soap:operation soapAction="http://tempuri.org/IFoobar/GetDataUsingDataContract" style="document"/>
    <wsdl:input>
    <soap:body use="literal"/>
    </wsdl:input>
    <wsdl:output>
    <soap:body use="literal"/>
    </wsdl:output>
    </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="Foobar">
    <wsdl:port name="BasicHttpBinding_IFoobar" binding="tns:BasicHttpBinding_IFoobar">
    <soap:address location="http://172.16.12.64/Wcf/Service1.svc"/>
    </wsdl:port>
    </wsdl:service>
    </wsdl:definitions>

    可以看到WSDL是一个标准的XML文件,此外它还拥有XSD定义(xs namespace)、SOAP数据(soap namespace),WSDL数据(wsdl namespace),通过这样的的一个xml文件,由于已经定义好的约定,我们便能够明白它提供什么样的服务和操作。

  2. 那么现在既然知道了我能够提供什么样的服务,那么如果你想要使用我提供的服务,你就得给我发送请求数据包,我收到你的请求后,会再返回给你响应包,这个数据发送和响应的过程需要使用双方已经约定好的数据包格式,这样双方才能明白对方到底在说什么,那么在WCF中双方交换数据的标准就是SOAP(也称为协议,所谓协议就是双方约定好的东西)。在上面的WSDL里你也可以看到,对于某一个operation,会告诉你它对应的action是什么。你发送的SOAP请求首先它是一个标准的HTTP请求(当然你也可以直接通过TCP发送),在它的BODY里面你要提供一个符合SOAP规范的XML格式的 Envelope,在这个Envelope里你要描述你请求的action,argument, etc.  我们来看一个post方式的soap请求:
    POST http://172.16.12.64/Wcf/Service1.svc HTTP/1.1
    Content-Type: text/xml; charset=utf-8
    SOAPAction: "http://tempuri.org/IFoobar/GetData"
    Host: 172.16.12.64
    Content-Length: 158
    Expect: 100-continue
    Accept-Encoding: gzip, deflate
    
    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetData xmlns="http://tempuri.org/"><value>12</value></GetData></s:Body></s:Envelope>
  3. 当你把请求发给我后,我们来看看我是给你返回了什么样的响应:
    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Type: text/xml; charset=utf-8
    Vary: Accept-Encoding
    Server: Microsoft-IIS/10.0
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 26 May 2017 08:51:52 GMT
    Content-Length: 203
    
    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetDataResponse xmlns="http://tempuri.org/"><GetDataResult>You entered: 12</GetDataResult></GetDataResponse></s:Body></s:Envelope>
  4. 上面说的是WCF生成的WSDL,以及SOAP交易过程。那我们再来看看Web Service又有什么不同,大家知道在很久很久之前,在微软发明WCF之前,大家如果要进行远程过程调用(RPC),通常有这样几种技术:Remoting、DCom、Web Services,这其中Remoting和DCom又要求Consumer端也必须是运行在Windows平台上,所以在那个时候如果你想你的服务能够被别人在Linux下使用,那么你就得将它作为Web Services进行发布。其实Web Service和WCF在SOAP交易层面看的话,基本上是差不多的(并且WCF本身也是可以通过一些标记来作为标准Web Service进行发布),首先Web Service也是通过发布WSDL来告诉别人它提供什么样的服务,让我们来看一个Web Service产生的WSDL文件:
    <wsdl:definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://tempuri.org/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://tempuri.org/">
    <wsdl:types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
    <s:element name="GetData">
    <s:complexType>
    <s:sequence>
    <s:element minOccurs="1" maxOccurs="1" name="value" type="s:int"/>
    </s:sequence>
    </s:complexType>
    </s:element>
    <s:element name="GetDataResponse">
    <s:complexType>
    <s:sequence>
    <s:element minOccurs="0" maxOccurs="1" name="GetDataResult" type="s:string"/>
    </s:sequence>
    </s:complexType>
    </s:element>
    </s:schema>
    </wsdl:types>
    <wsdl:message name="GetDataSoapIn">
    <wsdl:part name="parameters" element="tns:GetData"/>
    </wsdl:message>
    <wsdl:message name="GetDataSoapOut">
    <wsdl:part name="parameters" element="tns:GetDataResponse"/>
    </wsdl:message>
    <wsdl:portType name="Foobar1Soap">
    <wsdl:operation name="GetData">
    <wsdl:input message="tns:GetDataSoapIn"/>
    <wsdl:output message="tns:GetDataSoapOut"/>
    </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="Foobar1Soap" type="tns:Foobar1Soap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="GetData">
    <soap:operation soapAction="http://tempuri.org/GetData" style="document"/>
    <wsdl:input>
    <soap:body use="literal"/>
    </wsdl:input>
    <wsdl:output>
    <soap:body use="literal"/>
    </wsdl:output>
    </wsdl:operation>
    </wsdl:binding>
    <wsdl:binding name="Foobar1Soap12" type="tns:Foobar1Soap">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="GetData">
    <soap12:operation soapAction="http://tempuri.org/GetData" style="document"/>
    <wsdl:input>
    <soap12:body use="literal"/>
    </wsdl:input>
    <wsdl:output>
    <soap12:body use="literal"/>
    </wsdl:output>
    </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="Foobar1">
    <wsdl:port name="Foobar1Soap" binding="tns:Foobar1Soap">
    <soap:address location="http://172.16.12.64/Wcf/foobar.asmx"/>
    </wsdl:port>
    <wsdl:port name="Foobar1Soap12" binding="tns:Foobar1Soap12">
    <soap12:address location="http://172.16.12.64/Wcf/foobar.asmx"/>
    </wsdl:port>
    </wsdl:service>
    </wsdl:definitions>
  5. 再来看看如果向Web Service发送一个POST请求,产生的SOAP Envelope是什么:
    POST http://172.16.12.64/Wcf/Foobar.asmx HTTP/1.1
    Content-Type: text/xml; charset=utf-8
    SOAPAction: "http://tempuri.org/GetData"
    Host: 172.16.12.64
    Content-Length: 210
    Expect: 100-continue
    Accept-Encoding: gzip, deflate
    
    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetData xmlns="http://tempuri.org/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><value>12</value></GetData></s:Body></s:Envelope>
    
    
  6. 我们再来看看返回的响应是什么:
    HTTP/1.1 200 OK
    Cache-Control: private, max-age=0
    Content-Type: text/xml; charset=utf-8
    Vary: Accept-Encoding
    Server: Microsoft-IIS/10.0
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 26 May 2017 09:31:29 GMT
    Content-Length: 355
    
    <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><GetDataResponse xmlns="http://tempuri.org/"><GetDataResult>You entered: 12</GetDataResult></GetDataResponse></soap:Body></soap:Envelope>

Leave a Reply

Your email address will not be published. Required fields are marked *