在一个新工作里,总要学习点什么,这个企业里使用的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服务的。
- 首先你如果要使用别人的服务,那么你得知道别人提供什么服务,反过来讲就是我得告诉你我有什么服务,那么在这点上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文件,由于已经定义好的约定,我们便能够明白它提供什么样的服务和操作。
- 那么现在既然知道了我能够提供什么样的服务,那么如果你想要使用我提供的服务,你就得给我发送请求数据包,我收到你的请求后,会再返回给你响应包,这个数据发送和响应的过程需要使用双方已经约定好的数据包格式,这样双方才能明白对方到底在说什么,那么在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>
- 当你把请求发给我后,我们来看看我是给你返回了什么样的响应:
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>
- 上面说的是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>
- 再来看看如果向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>
- 我们再来看看返回的响应是什么:
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>