[ Home | Syllabus | Course Notes | Assignments | Search]
Advantages of WSDL (Web Services Description Language)/WSML (Web Services Meta Language)
(an aside URL, URI, URN)
HTTP Request
POST /foobar HTTP/1.1 Host: 209.110.197.12 Content-Type: text/plain Content-Length: 12 Hello, World
HTTP Response
200 OK Content-Type: text/plain Content-Length: 12 dlroW ,olleH
////Here is a "C" program which would produce
the correct response
//// notice it uses standard input (for the request body) and output (for
response)
#include <stdio.h> int main(int argc, char **argv) { char buf[4096]; int cb = read(0, buf, sizeof(buf)); buf[cb] = 0; strrev(buf); printf("200 OK\r\n"); printf("Content-Type: text/plain\r\n"); printf("Content-Length: %d\r\n", cb); printf("\r\n"); printf(buf); return 0; }
POST /string_server/Object17 HTTP/1.1 Host: 209.110.197.2 Content-Type: text/xml Content-Length: 152 SOAPMethodName: urn:strings-com:IString#reverse <Envelope> <Body> <m:reverse xmlns:m='urn:strings-com:IString'> <theString>Hello, World</theString> </m:reverse> </Body> </Envelope>
200 OK Content-Type: text/xml Content-Length: 162 <Envelope> <Body> <m:reverseResponse xmlns:m='urn:strings-com:IString'> <result>dlroW ,olleH</result> </m:reverseResponse> </Body> </Envelope>
[ uuid(DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA) ] interface IBank : IUnknown { HRESULT withdraw([in] long account, [out] float *newBalance, [in, out] float *amount [out, retval] VARIANT_BOOL *overdrawn); } //// encode the input as a structure
struct withdraw { long account; float amount; }; //// and the output also
struct withdrawResponse { float newBalance; float amount; VARIANT_BOOL overdrawn; }; //// here is a visual basic program to invoke the COM object
Dim bank as IBank Dim amount as Single Dim newBal as Single Dim overdrawn as Boolean amount = 100 Set bank = GetObject("soap:http://bofsoap.com/am") overdrawn = bank.withdraw(3512, amount, newBal)
//// Withdrawal Proxy - sort of what MIDL does for you HRESULT Proxy_withdraw(long account, float *newBalance, float *amount, VARIANT_BOOL *overdrawn) { // serialize [in] params into a struct withdraw request; request.account = account; request.amount = *amount; // send the request message proxy->Send(&request, sizeof(request)); // receive the response message withdrawResponse response = { 0 }; proxy->Receive(&response, sizeof(response)); // deserialize response struct into [out] params *newBalance = response.newBalance; *amount = response.amount; *overdrawn = response.overdrawn; return S_OK; // or fail }
//// now in SOAP (element-normal form (ENF).
<soap:Envelope xmlns:soap='urn:schemas-xmlsoap-org:soap.v1'> <soap:Body> <IBank:withdraw xmlns:IBank= 'urn:uuid:DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA'> <account>3512</account> <amount>100</amount> </IBank:withdraw> </soap:Body> </soap:Envelope>
<soap:Envelope xmlns:soap='urn:schemas-xmlsoap-org:soap.v1'> <soap:Body> <IBank:withdrawResponse xmlns:IBank= 'urn:uuid:DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA'> <newBalance>0</newBalance> <amount>5</amount> <overdrawn>true</overdrawn> </IBank:withdrawResponse> </soap:Body> </soap:Envelope>
//// schemas tell the structure of XML documents
<schema targetNamespace='urn:schemas-xmlsoap-org:soap.v1'> <element name='Envelope'> <type> <element name='Header' type='Header' minOccurs='0' /> <element name='Body' type='Body' minOccurs='1' /> </type> </element> </schema> ---------------
package com.bofsoap.IBank; public class adjustment { public int account; public float amount; } //// java to SOAP
<t:adjustment xmlns:t='urn:develop-com:java:com.bofsoap.IBank'> <account>3514</account> <amount>100.0</amount> </t:adjustment> ----------
Arrays
struct POINTLIST { long cElems; [size_is(cElems)] POINT points[]; };
<t:POINTLIST xmlns:t='uri for POINTLIST'> <cElems>3</cElems> <points xsd:type='t:POINT[3]' > <POINT><x>3</x><y>4</y></POINT> <POINT><x>7</x><y>5</y></POINT> <POINT><x>1</x><y>9</y></POINT> </points> <t:POINTLIST> ----------
Faults
<schema targetNamespace='urn:schemas-xmlsoap-org:soap.v1' > <element name='Fault'> <type> <element name='faultcode' type='string' /> <element name='faultstring' type='string' /> <element name='runcode' type='string' /> <element name='detail' /> </type> </element> </schema>
<soap:Envelope xmlns:soap='urn:schemas-xmlsoap-org:soap.v1'> <soap:Body> <soap:Fault> <faultcode>400</faultcode> <faultstring> Divide by zero occurred </faultstring> <runcode>Maybe</runcode> <detail> <t:DivideByZeroException xmlns:t="someURI"> <expression>x = 2 / 0;<expression> </t:DivideByZeroException> </detail> </soap:Fault> </soap:Body> </soap:Envelope>
Rich set of examples - both on server and client side.
We will focus on C++ server side (because COM based)
There are two versions: one that is basically a classic COM object (in the
"rpc" folders)
and one that uses the low level SOAP APIs (in the "srsz" folders).|
First we look at the high-level classic COM implementation of the server
IDL file
//// First the C++ COM (server) object
using the high level protocol
//// This IDL is standard COM
// CalcSvcRpcCpp.idl : IDL source for CalcSvcRpcCpp.dll // // This file will be processed by the MIDL tool to // produce the type library (CalcSvcRpcCpp.tlb) and marshalling code. import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(320A1D8F-23D3-4BDC-B1AA-412CF8D30E70), dual, helpstring("ICalc Interface"), pointer_default(unique) ] interface ICalc : IDispatch { [id(1), helpstring("method Add")] HRESULT Add([in] double A, [in] double B, [out, retval] double* Result); [id(2), helpstring("method Subtract")] HRESULT Subtract([in] double A, [in] double B, [out, retval] double* Result); [id(3), helpstring("method Divide")] HRESULT Divide([in] double A, [in] double B, [out, retval] double* Result); [id(4), helpstring("method Multiply")] HRESULT Multiply([in] double A, [in] double B, [out, retval] double* Result); }; [ uuid(D32945E1-9412-4652-B123-7B04AD454DDC), version(1.0), helpstring("CalcSvcRpcCpp 1.0 Type Library") ] library CALCSVCRPCCPPLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(699815E5-6F9E-403B-8C35-53476BABA89E), helpstring("Calc Class") coclass Calc { [default] interface ICalc; }; };
//// standard COM implementation - nothing SOAPy here
// Calc.cpp : Implementation of CCalc #include "stdafx.h" #include "CalcSvcRpcCpp.h" #include "Calc.h" ///////////////////////////////////////////////////////////////////////////// // CCalc STDMETHODIMP CCalc::Add(double A, double B, double *Result) { *Result = A + B; return S_OK; } STDMETHODIMP CCalc::Subtract(double A, double B, double *Result) { *Result = A - B; return S_OK; } STDMETHODIMP CCalc::Multiply(double A, double B, double *Result) { *Result = A * B; return S_OK; } STDMETHODIMP CCalc::Divide(double A, double B, double *Result) { *Result = A / B; return S_OK; }
<?xml version='1.0' encoding='UTF-8' ?> <servicemapping name='Calc'> <service name='Calc'> <using PROGID='CalcSvcRpcCpp.Calc.1' cachable='1' ID='CalcObject' /> <port name='CalcSoapPort'> <operation name='Multiply'> <execute uses='CalcObject' method='Multiply' dispID='4'> <parameter callIndex='1' name='A' elementName='A' /> <parameter callIndex='2' name='B' elementName='B' /> <parameter callIndex='-1' name='retval' elementName='Result' /> </execute> </operation> <operation name='Divide'> <execute uses='CalcObject' method='Divide' dispID='3'> <parameter callIndex='1' name='A' elementName='A' /> <parameter callIndex='2' name='B' elementName='B' /> <parameter callIndex='-1' name='retval' elementName='Result' /> </execute> </operation> <operation name='Subtract'> <execute uses='CalcObject' method='Subtract' dispID='2'> <parameter callIndex='1' name='A' elementName='A' /> <parameter callIndex='2' name='B' elementName='B' /> <parameter callIndex='-1' name='retval' elementName='Result' /> </execute> </operation> <operation name='Add'> <execute uses='CalcObject' method='Add' dispID='1'> <parameter callIndex='1' name='A' elementName='A' /> <parameter callIndex='2' name='B' elementName='B' /> <parameter callIndex='-1' name='retval' elementName='Result' /> </execute> </operation> </port> </service> </servicemapping>
<?xml version='1.0' encoding='UTF-8' ?> <definitions name ='Calc' targetNamespace = 'http://tempuri.org/wsdl/' xmlns:wsdlns='http://tempuri.org/wsdl/' xmlns:typens='http://tempuri.org/type' xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/' xmlns:xsd='http://www.w3.org/2000/10/XMLSchema' xmlns:stk='http://schemas.microsoft.com/soap-toolkit/wsdl-extension' xmlns='http://schemas.xmlsoap.org/wsdl/'> <types> <schema targetNamespace='http://tempuri.org/type' xmlns='http://www.w3.org/2000/10/XMLSchema' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'> </schema> </types> <message name='Calc.Multiply'> <part name='A' type='xsd:double'/> <part name='B' type='xsd:double'/> </message> <message name='Calc.MultiplyResponse'> <part name='Result' type='xsd:double'/> </message> <message name='Calc.Divide'> <part name='A' type='xsd:double'/> <part name='B' type='xsd:double'/> </message> <message name='Calc.DivideResponse'> <part name='Result' type='xsd:double'/> </message> <message name='Calc.Subtract'> <part name='A' type='xsd:double'/> <part name='B' type='xsd:double'/> </message> <message name='Calc.SubtractResponse'> <part name='Result' type='xsd:double'/> </message> <message name='Calc.Add'> <part name='A' type='xsd:double'/> <part name='B' type='xsd:double'/> </message> <message name='Calc.AddResponse'> <part name='Result' type='xsd:double'/> </message> <portType name='CalcSoapPort'> <operation name='Multiply' parameterOrder='A B'> <input message='wsdlns:Calc.Multiply' /> <output message='wsdlns:Calc.MultiplyResponse' /> </operation> <operation name='Divide' parameterOrder='A B'> <input message='wsdlns:Calc.Divide' /> <output message='wsdlns:Calc.DivideResponse' /> </operation> <operation name='Subtract' parameterOrder='A B'> <input message='wsdlns:Calc.Subtract' /> <output message='wsdlns:Calc.SubtractResponse' /> </operation> <operation name='Add' parameterOrder='A B'> <input message='wsdlns:Calc.Add' /> <output message='wsdlns:Calc.AddResponse' /> </operation> </portType> <binding name='CalcSoapBinding' type='wsdlns:CalcSoapPort' > <stk:binding preferredEncoding='UTF-8'/> <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http' /> <operation name='Multiply' > <soap:operation soapAction='http://tempuri.org/action/Calc.Multiply' /> <input> <soap:body use='encoded' namespace='http://tempuri.org/message/' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' /> </input> <output> <soap:body use='encoded' namespace='http://tempuri.org/message/' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' /> </output> </operation> <operation name='Divide' > <soap:operation soapAction='http://tempuri.org/action/Calc.Divide' /> <input> <soap:body use='encoded' namespace='http://tempuri.org/message/' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' /> </input> <output> <soap:body use='encoded' namespace='http://tempuri.org/message/' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' /> </output> </operation> <operation name='Subtract' > <soap:operation soapAction='http://tempuri.org/action/Calc.Subtract' /> <input> <soap:body use='encoded' namespace='http://tempuri.org/message/' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' /> </input> <output> <soap:body use='encoded' namespace='http://tempuri.org/message/' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' /> </output> </operation> <operation name='Add' > <soap:operation soapAction='http://tempuri.org/action/Calc.Add' /> <input> <soap:body use='encoded' namespace='http://tempurSoapg/message/' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' /> </input> <output> <soap:body use='encoded' namespace='http://tempuri.org/message/' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' /> </output> </operation> </binding> <service name='Calc' > <port name='CalcSoapPort' binding='wsdlns:CalcSoapBinding' > <soap:address location='http://MSSoapSampleServer/MSSoapSamples/Calc/Service/Rpc/IsapiCpp/Calc.wsdl' /> </port> </service> </definitions>
// CalcSvcSrSzCpp.idl : IDL source for CalcSvcSrSzCpp.dll interface ICalc : IDispatch { [id(1), helpstring("method Process")]HRESULT Process( [in] VARIANT Request, [in] VARIANT Response, [out, retval] VARIANT_BOOL* pSuccess); };
//// this is an "interpreted" call built on the fly!
//// I removed the error checking to simplify the logic
// Calc.cpp : Implementation of CCalc #include "stdafx.h" #include "CalcSvcSrSzCpp.h" #include "Calc.h" #include "stdio.h" ///////////////////////////////////////////////////////////////////////////// // CCalc LPCTSTR CCalc::WRAPPER_ELEMENT_NAMESPACE = _T("http://tempuri.org/message/"); STDMETHODIMP CCalc::Process( VARIANT varRequest, VARIANT varResponse, VARIANT_BOOL* pbSuccess) { HRESULT hr; _variant_t varA; _variant_t varB; _variant_t varAnswer; ISoapSerializerPtr pSerializer; ISoapReaderPtr pReader; _bstr_t bstrMethodName; IXMLDOMElementPtr pParameter; *pbSuccess = VARIANT_FALSE; hr = pReader.CreateInstance(__uuidof(SoapReader)); pReader->Load(varRequest, ""); bstrMethodName = pReader->RPCStruct->baseName; pParameter = pReader->RPCParameter["A"][""]; varA = pParameter->text; varA.ChangeType(VT_R8); pParameter = pReader->RPCParameter["B"][""]; varB = pParameter->text; varB.ChangeType(VT_R8); if (bstrMethodName == _bstr_t(_T("Add"))){ varAnswer = varA.dblVal + varB.dblVal; } else if (bstrMethodName == _bstr_t(_T("Subtract"))) { varAnswer = varA.dblVal - varB.dblVal; } varAnswer.ChangeType(VT_BSTR); hr = pSerializer.CreateInstance(__uuidof(SoapSerializer)); pSerializer->Init(varResponse); pSerializer->startEnvelope(_bstr_t(), _bstr_t(), _bstr_t()); pSerializer->startBody(_bstr_t()); pSerializer->startElement(bstrMethodName + _T("Response"), WRAPPER_ELEMENT_NAMESPACE, _bstr_t(), _T("m")); pSerializer->startElement(_T("Result"), _bstr_t(), _bstr_t(), _bstr_t()); pSerializer->writeString(_bstr_t(varAnswer)); pSerializer->endElement(); pSerializer->endElement(); pSerializer->endBody(); pSerializer->endEnvelope(); *pbSuccess = VARIANT_TRUE; return S_OK; }