Matthew
Matthew

Reputation: 105

Wrap XML in SOAP envelope in .net

I need help with wrapping an XML in a SOAP envelope for a third party SOAP server. The third party has provided xsd files for the inbound request and outbound response. I've taken those XSD files and created C# classes of them using the xsd tool. My problem is that I need to wrap the serialized request with a SOAP envelope and I don't know where to start. I was looking at the Microsoft Web Service Enhancements 3, but that says that it's only for .net 2.0 and VS2005. I am using VS2012 and .net 4.5. Also, I've looked into connecting to the server by way of web service but it doesn't seem compatible and does not have a WSDL.

The following is a sample of what the SOAP server expects for an inbound request.

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Body>
<GetBasicData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="CRS610MI">
<CONO xmlns="">1</CONO>
<CUNO xmlns="">12345</CUNO>
</GetBasicData>
</soap:Body>
</soap:Envelope>

This is what the serialized XML string looks like.

<?xml version="1.0" encoding="utf-8"?>
<GetBasicData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="CRS610MI">
<CONO xmlns="">1</CONO>
<CUNO xmlns="">12345</CUNO>
</GetBasicData>

Code I'm using for my web request and response.

Byte[] byteArray = System.Text.UTF8Encoding.UTF8.GetBytes(data);

WebRequest webRequest = WebRequest.Create(@"http://myserver:8888");
webRequest.ContentLength = byteArray.Length;
webRequest.ContentType = @"text/xml; charset=utf-8";
webRequest.Headers.Add("SOAPAction", @"http://schemas.xmlsoap.org/soap/envelope/");
webRequest.Method = "POST";

Stream requestStream = webRequest.GetRequestStream();
requestStream.Write(byteArray, 0, byteArray.Length);
requestStream.Close();
requestStream.Dispose();

WebResponse webResponse = webRequest.GetResponse();

Stream responseStream = webResponse.GetResponseStream();

StreamReader streamReader = new StreamReader(responseStream);

String line;

while ((line = streamReader.ReadLine()) != null)
{
    Debug.WriteLine(line);
}

I've tested my code by replacing my serialized string with the text in the sample file provided by the third party and it worked as expected. I also took my serialized string and inserted the envelope text in the correct places and that also worked, web request went through and I got the response I was looking for. Short of inserting the envelope text into my serialized string manually what can I do. I have to imagine there's a method or class that will take care of this for me in a standardized way?

Upvotes: 6

Views: 42105

Answers (4)

Mauro Torres
Mauro Torres

Reputation: 751

I don’t understand why some are so resolute that you HAVE to use the WSDL. The WSDL is simply so that you can call their methods, and use their entities, and is optional.

I’ve gotten this to work by going to the exact url for their service, such as https://something.com/SomeService/TheService.asmx?method=DoSomething

And just analyzing the exact header and XML their API defines, including the SOAP envelope. The first section should be the header information, and then below that is the XML. Create the XML exactly how they specify, including the envelope tags. For the Header: POST, Host, Content-Type and SOAPAction, are the most important. I’ve found that some servers don’t need Content-Length, even though its listed in their API.

Here is some sample code

public void PostXMLData(string serviceUrl, XDocument requestXML)
    {
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(serviceUrl);
        req.Method = "POST";
        req.Host = "SOMETHING";
        req.ContentType = "text/xml; charset=utf-8";
        req.Headers.Add("SOAPAction", "SOMETHING");
        req.Accept = "text/xml";

        using (Stream stream = req.GetRequestStream())
        {
            requestXML.Save(stream);
        }

        using (WebResponse resp = req.GetResponse())
        {
            using (StreamReader rd = new StreamReader(resp.GetResponseStream()))
            {
                var result = rd.ReadToEnd();
                if (result.Contains("error"))
                {
                    throw new Exception($"XML Submission Failed: {result}");
                }
            }
        }
    }

Upvotes: 0

Matthew
Matthew

Reputation: 105

I was able to solve this by using an XLST to wrap the XML in soap.

Here's my working test app code

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.Xml.Xsl;


namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            GetBasicData getBasicData = new GetBasicData();
            getBasicData.CONO = "1";
            getBasicData.CUNO = "201702";

            XPathDocument requestXPathDocument;

            if (SerializeIntoRequestXPathDocument(getBasicData, out requestXPathDocument))
            {
                XmlDocument requestXmlDocument;

                if (CreateRequestXMLDocument(requestXPathDocument, @"Z:\Darice\M3 SOAP\GetBasicData.xsl", out requestXmlDocument))
                {
                    XmlDocument responseXmlDocument;

                    if (ExecuteRequestSoap(requestXmlDocument, out responseXmlDocument))
                    {
                        MemoryStream unwrappedMemoryStream;

                        if (UnwrapSoapResponseXmlDocumentIntoMemoryStream(responseXmlDocument, out unwrappedMemoryStream))
                        {
                            GetBasicDataResponse getBasicDataResponse;

                            if (!DeserializeResponseMemoryStream(unwrappedMemoryStream, out getBasicDataResponse))
                            {
                                Debug.WriteLine("FAIL");
                            }
                        }
                    }
                }
            }

            Console.ReadLine();
        }


        //STATIC FUNCTIONS
        private static Boolean CreateRequestXMLDocument(XPathDocument xPathDocument, String xslPath, out XmlDocument xmlDocument)
        {
            try
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    using (StreamWriter streamWriter = new StreamWriter(memoryStream))
                    {
                        XmlWriter xmlWriter = XmlWriter.Create(streamWriter);

                        XsltSettings xsltSettings = new XsltSettings();
                        xsltSettings.EnableScript = true;

                        XslCompiledTransform xslCompiledTransform = new XslCompiledTransform();
                        xslCompiledTransform.Load(xslPath, xsltSettings, null);
                        xslCompiledTransform.Transform(xPathDocument, xmlWriter);

                        memoryStream.Position = 0;

                        using (StreamReader streamReader = new StreamReader(memoryStream))
                        {
                            XmlReader xmlReader = XmlReader.Create(streamReader);

                            xmlDocument = new XmlDocument();
                            xmlDocument.Load(xmlReader);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);

                xmlDocument = null;

                return false;
            }

            return true;
        }

        private static Boolean DeserializeResponseMemoryStream<T>(MemoryStream memoryStream, out T xmlObject)
        {
            try
            {
                using (StreamReader streamReader = new StreamReader(memoryStream))
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

                    using (XmlReader xmlReader = XmlReader.Create(streamReader))
                    {
                        xmlObject = (T)xmlSerializer.Deserialize(xmlReader);
                    }
                }
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);

                xmlObject = default(T);

                return false;
            }

            return true;
        }

        private static Boolean ExecuteRequestSoap(XmlDocument requestXmlDocument, out XmlDocument responseXmlDocument)
        {
            try
            {
                Byte[] byteArray = UTF8Encoding.UTF8.GetBytes(requestXmlDocument.OuterXml);

                WebRequest webRequest = WebRequest.Create(Properties.Resources.SoapServerAddress);
                webRequest.ContentLength = byteArray.Length;
                webRequest.ContentType = @"text/xml; charset=utf-8";
                webRequest.Headers.Add("SOAPAction", @"http://schemas.xmlsoap.org/soap/envelope/");
                webRequest.Method = "POST";

                using (Stream requestStream = webRequest.GetRequestStream())
                {
                    requestStream.Write(byteArray, 0, byteArray.Length);

                    using (WebResponse webResponse = webRequest.GetResponse())
                    {
                        using (Stream responseStream = webResponse.GetResponseStream())
                        {
                            using (StreamReader streamReader = new StreamReader(responseStream))
                            {
                                responseXmlDocument = new XmlDocument();
                                responseXmlDocument.LoadXml(streamReader.ReadToEnd());
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);

                responseXmlDocument = null;

                return false;
            }

            return true;
        }

        private static Boolean SerializeIntoRequestXPathDocument<T>(T dataObject, out XPathDocument xPathDocument)
        {
            try
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

                using (MemoryStream memoryStream = new MemoryStream())
                {
                    using (StreamWriter streamWriter = new StreamWriter(memoryStream))
                    {
                        xmlSerializer.Serialize(streamWriter, dataObject);

                        memoryStream.Position = 0;

                        using (StreamReader streamReader = new StreamReader(memoryStream))
                        {
                            memoryStream.Position = 0;

                            xPathDocument = new XPathDocument(streamReader);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);

                xPathDocument = null;

                return false;
            }

            return true;
        }

        private static Boolean UnwrapSoapResponseXmlDocumentIntoMemoryStream(XmlDocument responseXmlDocument, out MemoryStream memoryStream)
        {
            try
            {
                XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(responseXmlDocument.NameTable);
                xmlNamespaceManager.AddNamespace("tns", "CRS610MI");
                xmlNamespaceManager.AddNamespace("SOAP", @"http://schemas.xmlsoap.org/soap/envelope/");

                XmlNode xmlNode = responseXmlDocument.SelectSingleNode(@"/SOAP:Envelope/SOAP:Body/tns:GetBasicDataResponse", xmlNamespaceManager);

                memoryStream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(xmlNode.OuterXml));
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);

                memoryStream = null;

                return false;
            }

            return true;
        }
    }
}

And here's the XSL code

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:crs="CRS610MI" exclude-result-prefixes="crs">
<xsl:output method="xml"/>
<xsl:template match="/">
  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <soap:Body>
    <xsl:apply-templates select="node()|@*"/>
    </soap:Body>
  </soap:Envelope>
</xsl:template>
<xsl:template match="node()|@*">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Upvotes: 3

John Saunders
John Saunders

Reputation: 161783

If the service provider does not provide a WSDL, then you should not do business with them. It's not rocket science, and it is the standard way that SOAP web services work. It's only been a standard for over a decade. I would seriously wonder in what other way this service provider is incompetent.

If you have no choice but to do business with incompetent business partners, then

  1. Good luck to you
  2. Create your own WSDL to match their service, then use "Add Service Reference" to create the proxy classes necessary to communicate to the service.

Upvotes: 1

TYY
TYY

Reputation: 2716

You need to mark your class with MessageContract for soap

[MessageContract]
public class ExampleResponse
{
   private string _myResponse = String.Empty;

   [MessageBodyMember(Name = "ResponseToGive", Namespace = "http://myserver:8888")]
   public string ResponseToGive
   {
     get { return _myResponse; }
     set { _myResponse = value; }
   }
}

Upvotes: 0

Related Questions