Client to send SOAP request and receive response

Trying to create a C# client (will be developed as a Windows service) that sends SOAP requests to a web service (and gets the results).

From this question I saw this code:

protected virtual WebRequest CreateRequest(ISoapMessage soapMessage)
{ var wr = WebRequest.Create(soapMessage.Uri); wr.ContentType = "text/xml;charset=utf-8"; wr.ContentLength = soapMessage.ContentXml.Length; wr.Headers.Add("SOAPAction", soapMessage.SoapAction); wr.Credentials = soapMessage.Credentials; wr.Method = "POST"; wr.GetRequestStream().Write(Encoding.UTF8.GetBytes(soapMessage.ContentXml), 0, soapMessage.ContentXml.Length); return wr;
}
public interface ISoapMessage
{ string Uri { get; } string ContentXml { get; } string SoapAction { get; } ICredentials Credentials { get; }
}

Looks nice, anyone knows how to use it and if it is the best practice?

8 Answers

I normally use another way to do the same

using System.Xml;
using System.Net;
using System.IO;
public static void CallWebService()
{ var _url = ""; var _action = ""; XmlDocument soapEnvelopeXml = CreateSoapEnvelope(); HttpWebRequest webRequest = CreateWebRequest(_url, _action); InsertSoapEnvelopeIntoWebRequest(soapEnvelopeXml, webRequest); // begin async call to web request. IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null); // suspend this thread until call is complete. You might want to // do something usefull here like update your UI. asyncResult.AsyncWaitHandle.WaitOne(); // get the response from the completed web request. string soapResult; using (WebResponse webResponse = webRequest.EndGetResponse(asyncResult)) { using (StreamReader rd = new StreamReader(webResponse.GetResponseStream())) { soapResult = rd.ReadToEnd(); } Console.Write(soapResult); }
}
private static HttpWebRequest CreateWebRequest(string url, string action)
{ HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url); webRequest.Headers.Add("SOAPAction", action); webRequest.ContentType = "text/xml;charset=\"utf-8\""; webRequest.Accept = "text/xml"; webRequest.Method = "POST"; return webRequest;
}
private static XmlDocument CreateSoapEnvelope()
{ XmlDocument soapEnvelopeDocument = new XmlDocument(); soapEnvelopeDocument.LoadXml( @"<SOAP-ENV:Envelope xmlns:SOAP-ENV="""" xmlns:xsi="""" xmlns:xsd=""""> <SOAP-ENV:Body> <HelloWorld xmlns="""" SOAP-ENV:encodingStyle=""""> <int1 xsi:type=""xsd:integer"">12</int1> <int2 xsi:type=""xsd:integer"">32</int2> </HelloWorld> </SOAP-ENV:Body> </SOAP-ENV:Envelope>"); return soapEnvelopeDocument;
}
private static void InsertSoapEnvelopeIntoWebRequest(XmlDocument soapEnvelopeXml, HttpWebRequest webRequest)
{ using (Stream stream = webRequest.GetRequestStream()) { soapEnvelopeXml.Save(stream); }
}
16

I got this simple solution here:

Sending SOAP request and receiving response in .NET 4.0 C# without using the WSDL or proxy classes:

class Program { /// <summary> /// Execute a Soap WebService call /// </summary> public static void Execute() { HttpWebRequest request = CreateWebRequest(); XmlDocument soapEnvelopeXml = new XmlDocument(); soapEnvelopeXml.LoadXml(@"<?xml version=""1.0"" encoding=""utf-8""?> <soap:Envelope xmlns:xsi="""" xmlns:xsd="""" xmlns:soap=""""> <soap:Body> <HelloWorld xmlns="""" /> </soap:Body> </soap:Envelope>"); using (Stream stream = request.GetRequestStream()) { soapEnvelopeXml.Save(stream); } using (WebResponse response = request.GetResponse()) { using (StreamReader rd = new StreamReader(response.GetResponseStream())) { string soapResult = rd.ReadToEnd(); Console.WriteLine(soapResult); } } } /// <summary> /// Create a soap webrequest to [Url] /// </summary> /// <returns></returns> public static HttpWebRequest CreateWebRequest() { HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(@""); webRequest.Headers.Add(@"SOAP:Action"); webRequest.ContentType = "text/xml;charset=\"utf-8\""; webRequest.Accept = "text/xml"; webRequest.Method = "POST"; return webRequest; } static void Main(string[] args) { Execute(); } }
5

I think there is a simpler way:

 public async Task<string> CreateSoapEnvelope() { string soapString = @"<?xml version=""1.0"" encoding=""utf-8""?> <soap:Envelope xmlns:xsi="""" xmlns:xsd="""" xmlns:soap=""""> <soap:Body> <HelloWorld xmlns="""" /> </soap:Body> </soap:Envelope>"; HttpResponseMessage response = await PostXmlRequest("your_url_here", soapString); string content = await response.Content.ReadAsStringAsync(); return content; } public static async Task<HttpResponseMessage> PostXmlRequest(string baseUrl, string xmlString) { using (var httpClient = new HttpClient()) { var httpContent = new StringContent(xmlString, Encoding.UTF8, "text/xml"); httpContent.Headers.Add("SOAPAction", ""); return await httpClient.PostAsync(baseUrl, httpContent); } }
2

The best practice is to reference the WSDL and use it like a web service reference. It's easier and works better, but if you don't have the WSDL, the XSD definitions are a good piece of code.

6

I wrote a more general helper class which accepts a string-based dictionary of custom parameters, so that they can be set by the caller without having to hard-code them. It goes without saying that you should only use such method when you want (or need) to manually issue a SOAP-based web service: in most common scenarios the recommended approach would be using the Web Service WSDL together with the Add Service Reference Visual Studio feature instead.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Xml;
namespace Ryadel.Web.SOAP
{ /// <summary> /// Helper class to send custom SOAP requests. /// </summary> public static class SOAPHelper { /// <summary> /// Sends a custom sync SOAP request to given URL and receive a request /// </summary> /// <param name="url">The WebService endpoint URL</param> /// <param name="action">The WebService action name</param> /// <param name="parameters">A dictionary containing the parameters in a key-value fashion</param> /// <param name="soapAction">The SOAPAction value, as specified in the Web Service's WSDL (or NULL to use the url parameter)</param> /// <param name="useSOAP12">Set this to TRUE to use the SOAP v1.2 protocol, FALSE to use the SOAP v1.1 (default)</param> /// <returns>A string containing the raw Web Service response</returns> public static string SendSOAPRequest(string url, string action, Dictionary<string, string> parameters, string soapAction = null, bool useSOAP12 = false) { // Create the SOAP envelope XmlDocument soapEnvelopeXml = new XmlDocument(); var xmlStr = (useSOAP12) ? @"<?xml version=""1.0"" encoding=""utf-8""?> <soap12:Envelope xmlns:xsi="""" xmlns:xsd="""" xmlns:soap12=""""> <soap12:Body> <{0} xmlns=""{1}"">{2}</{0}> </soap12:Body> </soap12:Envelope>" : @"<?xml version=""1.0"" encoding=""utf-8""?> <soap:Envelope xmlns:soap="""" xmlns:xsi="""" xmlns:xsd=""""> <soap:Body> <{0} xmlns=""{1}"">{2}</{0}> </soap:Body> </soap:Envelope>"; string parms = string.Join(string.Empty, parameters.Select(kv => String.Format("<{0}>{1}</{0}>", kv.Key, kv.Value)).ToArray()); var s = String.Format(xmlStr, action, new Uri(url).GetLeftPart(UriPartial.Authority) + "/", parms); soapEnvelopeXml.LoadXml(s); // Create the web request HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url); webRequest.Headers.Add("SOAPAction", soapAction ?? url); webRequest.ContentType = (useSOAP12) ? "application/soap+xml;charset=\"utf-8\"" : "text/xml;charset=\"utf-8\""; webRequest.Accept = (useSOAP12) ? "application/soap+xml" : "text/xml"; webRequest.Method = "POST"; // Insert SOAP envelope using (Stream stream = webRequest.GetRequestStream()) { soapEnvelopeXml.Save(stream); } // Send request and retrieve result string result; using (WebResponse response = webRequest.GetResponse()) { using (StreamReader rd = new StreamReader(response.GetResponseStream())) { result = rd.ReadToEnd(); } } return result; } }
}

For additional info & details regarding this class you can also read this post on my blog.

So this is my final code after googling for 2 days on how to add a namespace and make soap request along with the SOAP envelope without adding proxy/Service Reference

class Request
{ public static void Execute(string XML) { try { HttpWebRequest request = CreateWebRequest(); XmlDocument soapEnvelopeXml = new XmlDocument(); soapEnvelopeXml.LoadXml(AppendEnvelope(AddNamespace(XML))); using (Stream stream = request.GetRequestStream()) { soapEnvelopeXml.Save(stream); } using (WebResponse response = request.GetResponse()) { using (StreamReader rd = new StreamReader(response.GetResponseStream())) { string soapResult = rd.ReadToEnd(); Console.WriteLine(soapResult); } } } catch (Exception ex) { Console.WriteLine(ex); } } private static HttpWebRequest CreateWebRequest() { string ICMURL = System.Configuration.ConfigurationManager.AppSettings.Get("ICMUrl"); HttpWebRequest webRequest = null; try { webRequest = (HttpWebRequest)WebRequest.Create(ICMURL); webRequest.Headers.Add(@"SOAP:Action"); webRequest.ContentType = "text/xml;charset=\"utf-8\""; webRequest.Accept = "text/xml"; webRequest.Method = "POST"; } catch (Exception ex) { Console.WriteLine(ex); } return webRequest; } private static string AddNamespace(string XML) { string result = string.Empty; try { XmlDocument xdoc = new XmlDocument(); xdoc.LoadXml(XML); XmlElement temproot = xdoc.CreateElement("ws", "Request", ""); temproot.InnerXml = xdoc.DocumentElement.InnerXml; result = temproot.OuterXml; } catch (Exception ex) { Console.WriteLine(ex); } return result; } private static string AppendEnvelope(string data) { string head= @"<soapenv:Envelope xmlns:soapenv="""" ><soapenv:Header/><soapenv:Body>"; string end = @"</soapenv:Body></soapenv:Envelope>"; return head + data + end; }
}

As an alternative, and pretty close to debiasej approach. Since a SOAP request is just a HTTP request, you can simply perform a GET or POST using with HTTP client, but it's not mandatory to build SOAP envelope.

Something like this:

using Microsoft.Extensions.Logging;
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace HGF.Infraestructure.Communications
{ public class SOAPSample { private readonly IHttpClientFactory _clientFactory; private readonly ILogger<DocumentProvider> _logger; public SOAPSample(ILogger<DocumentProvider> logger, IHttpClientFactory clientFactory) { _clientFactory = clientFactory; _logger = logger; } public async Task<string> UsingGet(int value1, int value2) { try { var client = _clientFactory.CreateClient(); var response = await client.GetAsync($"", HttpCompletionOption.ResponseHeadersRead); //NULL check, HTTP Status Check.... return await response.Content.ReadAsStringAsync(); } catch (Exception ex) { _logger.LogError(ex, "Oops! Something went wrong"); return ex.Message; } } public async Task<string> UsingPost(int value1, int value2) { try { var content = new StringContent($"value1={value1}&value2={value2}", Encoding.UTF8, "application/x-www-form-urlencoded"); var client = _clientFactory.CreateClient(); var response = await client.PostAsync("", content); //NULL check, HTTP Status Check.... return await response.Content.ReadAsStringAsync(); } catch (Exception ex) { _logger.LogError(ex, "Oops! Something went wrong"); return ex.Message; } } }
}

Of course, it depends on your scenario. If the payload is too complex, then this won't work

Call SOAP webservice in c#

using (var client = new UpdatedOutlookServiceReferenceAPI.OutlookServiceSoapClient("OutlookServiceSoap"))
{ ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12; var result = client.UploadAttachmentBase64(GUID, FinalFileName, fileURL); if (result == true) { resultFlag = true; } else { resultFlag = false; } LogWriter.LogWrite1("resultFlag : " + resultFlag);
}
2

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like