Windows Systems Programming: Spring 2002

[ Home | Syllabus | Course Notes | Assignments | Search]


SOAP Calc Example (continued)

What we have seen so far:

SERVER:

Client:


WSDL for ASP server

Difference between WSDL for ASP server and WSDL for C++ server is the following line

<service name='Calc' >
    <port name='CalcSoapPort' binding='wsdlns:CalcSoapBinding' >
      <soap:address location='http://MSSoapSampleServer/MSSoapSamples/Calc/Service/SrSz/AspJs/Calc.asp' />
    </port>
</service>

C++ Client

Compare this to the JScript client above- although uses same access mechanism (high level RPC), more of the details are exposed in the C++ version.

Illustrates:

//// connect to a server to get WSDL
	HRESULT hr;

	CString NewWSDL;
	m_WSDLCtl.GetWindowText(NewWSDL);

	if(m_WSDLConnected != NewWSDL)
	{
		if (m_pSoapClient != NULL) m_pSoapClient.Release();
		hr = m_pSoapClient.CreateInstance(__uuidof(SoapClient));
		if(FAILED(hr))
		{
			DisplayHResult(_T("Cannot create SoapClient."), hr);
			return false;
		}
		hr = m_pSoapClient->mssoapinit((LPCTSTR)NewWSDL, _T(""), _T(""), _T(""));
		if(FAILED(hr))
		{
			DisplayFault(_T("Cannot initialize SoapClient. "));
			return false;
		}
		m_WSDLConnected = NewWSDL;
	}

//// Using the DISPATCH method - first get the ID in the "function" table
//// translates "pMethodName" string into "dispid" index 
hr = m_pSoapClient->GetIDsOfNames(IID_NULL, &pMethodName, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
//// Now set up the parameters as variants in variant array
DISPPARAMS dispparams;
VARIANTARG params[2];
VARIANT result;
CString ParamText;

VariantInit(&params[0]);
params[0].vt = VT_R8;
m_BCtl.GetWindowText(ParamText);
params[0].dblVal = atof(ParamText);

// Set A parameter.
VariantInit(&params[1]);
params[1].vt = VT_R8;
m_ACtl.GetWindowText(ParamText);
params[1].dblVal = atof(ParamText);

// Initialize DISPPARAMS structure.
dispparams.cArgs = 2;
dispparams.cNamedArgs = 0;
dispparams.rgdispidNamedArgs = NULL;
dispparams.rgvarg = params;

// Prepare result variant.
VariantInit(&result);
//// now call the method
hr = m_pSoapClient->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD,
	 &dispparams, &result, &ExceptInfo, NULL);
//// get the result
// Convert result to a string.
VariantChangeType(&result, &result, 0, VT_BSTR);

// Display result.
m_ResultCtl.SetWindowText(CString(result.bstrVal)); //// cast to CString
// Clean up variants.
VariantClear(&result);
VariantClear(&params[0]);
VariantClear(&params[1]);

Low level client

We will choose JSCRIPT first - easier to see the overall structure

var BASE_SOAP_ACTION_URI = "http://tempuri.org/action/Calc.";
Answer = Execute(END_POINT_URL, "Divide", 126, 3)
WScript.Echo("126/3=" + Answer);

function Execute(EndPointURL, Method, A, B)
{

    var Serializer;
    var Reader;
    var Connector;
    
    Connector = WScript.CreateObject("MSSOAP.HttpConnector");
    Connector.Property("EndPointURL") = EndPointURL;
    
    Connector.Property("SoapAction") = BASE_SOAP_ACTION_URI & Method;
    Connector.BeginMessage();
    
    Serializer = WScript.CreateObject("MSSOAP.SoapSerializer");
    Serializer.Init(Connector.InputStream); // attach HTTP connection to Serializer
    
    Serializer.startEnvelope();
    Serializer.startBody();
    Serializer.startElement(Method, WRAPPER_ELEMENT_NAMESPACE, "", "m");
    Serializer.startElement("A");
    Serializer.writeString(A);
    Serializer.endElement();
    Serializer.startElement("B");
    Serializer.writeString(B);
    Serializer.endElement();
    Serializer.endElement();
    Serializer.endBody();
    Serializer.endEnvelope();
    
    Connector.EndMessage();
    
    Reader = WScript.CreateObject("MSSOAP.SoapReader");
    Reader.Load(Connector.OutputStream); //// connect to SOAP response
    
    if( Reader.Fault == null )
    {
        return Reader.RPCResult.text;
    }
    else
    {
        return "FAULT: " + Reader.faultstring.text;
    }
    
}
//// basically the same in C++

Accessing COM using ASP instead of ISAPI

<%@ LANGUAGE=JScript %>
<%

  Response.ContentType = "text/xml"; //// set responce type
  
  if( Application("CalcAspJsCppServer") == void 0 ) 
  {
  
    Application.Lock();
    if( Application("CalcAspJsCppServer") == void 0 )
    {
    
      var SoapServer;
      var WSDLFilePath;
      var WSMLFilePath;
      
      WSDLFilePath = Server.MapPath("Calc.wsdl");
      WSMLFilePath = Server.MapPath("Calc.wsml");
      
      try
      {
        SoapServer = Server.CreateObject("MSSOAP.SoapServer");
      }
      catch(err)
      {
        SendFault("Cannot create SoapServer object. " + err.description + " (" + err.number + ")");
      }
    
      try
      {
        SoapServer.Init(WSDLFilePath, WSMLFilePath); //// use the WSDL info to parse
      }
      catch(err)
      {
        SendFault("SoapServer.Init failed. " & err.description);
      }
    
      Application("CalcAspJsCppServer") = SoapServer;
      
    }
    Application.UnLock();
    
  }

  SoapServer = Application("CalcAspJsCppServer");
  
  try
  {
    SoapServer.SoapInvoke(Request, Response, ""); 
//// if successful - done
  }
  catch(err)
  {
    SendFault("SoapServer.SoapInvoke failed. " & err.description);
  }
  
  function SendFault(LogMessage)
  {
    
    var Serializer;
    
    // "URI Query" logging must be enabled for AppendToLog to work
    
    Response.AppendToLog(" SOAP ERROR: " & LogMessage);
    
    try
    {
      Serializer = Server.CreateObject("MSSOAP.SoapSerializer");
    }
    catch(err)
    {  
      Response.AppendToLog("Could not create SoapSerializer object. " & err.description);
      Response.Status = "500 Internal Server Error";
      Response.End();
    }

    try
    {    
      Serializer.Init(Response);
    }
    catch(err)
    {
      Response.AppendToLog("SoapSerializer.Init failed. " & err.description);
      Response.Status = "500 Internal Server Error";
      Response.End();
    }

    try
    {      
      Serializer.startEnvelope("", "", "");
      Serializer.startBody("");
      Serializer.startFault("Server", "The request could not be processed due to a problem in the server. Please contact the system admistrator. " + LogMessage, "");
      Serializer.endFault();
      Serializer.endBody();
      Serializer.endEnvelope();
    }
    catch(err)
    {
      Response.AppendToLog("SoapSerializer failed. " & err.description);
      Response.Status = "500 Internal Server Error";
      Response.End();
    }          
      
    Response.End();
    
  }  
  
%>  

ASP using low level API

Actually it is the COM object that uses the low level API - the ASP is easy

////It directly accesses the COM object - and let's it parse the request and create the response

<%@ LANGUAGE=JScript %>
<%
	var CalcSrv = Server.CreateObject("CalcSvcSrSzCpp.Calc") ////COM progId
	Response.ContentType = "text/xml"
	if(!CalcSrv.Process(Request, Response))
	{
	  Response.Status = "500 Internal Server Error"
	}  
%>  

CPP low level server

// 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/");
//// removed error checking to point our main structure

STDMETHODIMP CCalc::Process(
	VARIANT varRequest,  //// input request stream
	VARIANT varResponse, //// output response stream
	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;
	}
	hr = pSerializer.CreateInstance(__uuidof(SoapSerializer));
	varAnswer.ChangeType(VT_BSTR);
	pSerializer->Init(varResponse); //// send to the output stream
	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;

}

Redirecting Server

soapclient = Server.CreateObject("MSSOAP.SoapClient")
soapclient.ClientProperty("ServerHTTPRequest") = true
soapclient.mssoapinit(WSDL_URL) //// need to connect to "real" service
//// now get request from form (normal ASP form handling
    a = Request.Form("A")
    b = Request.Form("B")		
    op = Request.Form("SubmitMath")



    if (op == "Add") {
        res = soapclient.Add(a, b)
    } else if (op == "Multiply") {
        res = soapclient.Multiply(a, b)
    } else if (op == "Subtract") {
        res = soapclient.Subtract(a, b)
    } else if (op == "Divide") {
        res = soapclient.Divide(a, b)
    }

<B>Result:</B> <%=res%><P><P> //// HTML embedded in ASP script "<% ... %>" is script escape
<a href="Form.htm">Calculate Again</a>

 

 

 


Copyright chris wild 1999-2002.
For problems or questions regarding this web contact [Dr. Wild].
Last updated: April 07, 2002.