• Skip to main content
  • Skip to primary sidebar

Technical Notes Of
Ehi Kioya

Technical Notes Of Ehi Kioya

  • About
  • Contact
MENUMENU
  • Blog Home
  • AWS, Azure, Cloud
  • Backend (Server-Side)
  • Frontend (Client-Side)
  • SharePoint
  • Tools & Resources
    • CM/IN Ruler
    • URL Decoder
    • Text Hasher
    • Word Count
    • IP Lookup
  • Linux & Servers
  • Zero Code Tech
  • WordPress
  • Musings
  • More
    Categories
    • Cloud
    • Server-Side
    • Front-End
    • SharePoint
    • Tools
    • Linux
    • Zero Code
    • WordPress
    • Musings
Home » Backend (Server-Side) » Working With Web Services Without Adding A Reference In C#

Working With Web Services Without Adding A Reference In C#

By Ehi Kioya 2 Comments

Generally, when writing code to communicate with web services in C#, the usual process is to add a service reference. Visual Studio then automatically creates all the wrapper classes/objects for you that you need in order to talk with the service. For the most part, this approach is great and really increases productivity because all the required web service “plumbing” is handled for you by the IDE. However, sometimes, you want greater control.

This article explains and gives you the code needed to work with a regular SOAP based web service without adding a reference.

But first, why would anyone want to give themselves more work?

Why You May Not Want A Web Service Reference

Sometimes, you would prefer to manually write all the code needed for authentication, method invoking, etc. An example of such a situation would be if you want your code to decide on which web service endpoint to connect to at runtime.

You could have slightly different copies of the web service on multiple servers (development, production, QA servers). And depending on the value of some variable defined in an external system (say SharePoint), your C# code then needs to dynamically point to the correct web service endpoint.

Another scenario when you may not want a reference is, if for some reason both the actual web service as well as the code to consume the service are in development at the same time.
 
So technically speaking, you don’t really have a web service (yet) since the production copy of the service has not been deployed.

While it is possible to dynamically change the url of a service reference like this…

var service = new MyService.MyWSSoapClient();
service.Endpoint.Address = new System.ServiceModel.EndpointAddress("http://localhost:8080/");

I have found that it is actually just more convenient, in some cases, to just work with the web service without adding a reference.

Now, let’s discuss the “how” part.

The “How” Part: Web Service Wrapper

Create a web service wrapper class like this:

public class MyWebService
{
	public string Url { get; set; }
	public string MethodName { get; set; }
	public string UserName { get; set; }
	public string Password { get; set; }
	public string SOAPAction { get; set; }
	public string WSSPasswordType { get; set; }
	public Dictionary<string, string> Params = new Dictionary<string, string>();
	public XDocument ResultXML;
	public string ResultString;

	public MyWebService()
	{
	}

	public MyWebService(string url, string methodName, string userName, string password, string soapAction, string wssPasswordType)
	{
		Url = url;
		MethodName = methodName;
		UserName = userName;
		Password = password;
		SOAPAction = soapAction;
		WSSPasswordType = wssPasswordType;
	}

	/// <summary>
	/// Invokes service
	/// </summary>
	public void Invoke()
	{
		Invoke(true);
	}

	/// <summary>
	/// Invokes service
	/// </summary>
	/// <param name="encode">Added parameters will encode? (default: true)</param>
	public void Invoke(bool encode)
	{
		string phrase = Guid.NewGuid().ToString();
		string tempPhrase = phrase.Replace("-", "");
		tempPhrase = tempPhrase.ToUpper();
		string userNameToken = "UsernameToken-" + tempPhrase;
		DateTime created = DateTime.Now;
		string createdStr = created.ToString("yyyy-MM-ddThh:mm:ss.fffZ");
		SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
		byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase));
		string nonce = Convert.ToBase64String(hashedDataBytes);
		string soapStr = "";

		if (WSSPasswordType == "PasswordText")
		{
			soapStr =
				@"<?xml version=""1.0"" encoding=""utf-8""?>
				<soap:Envelope xmlns:soap=""http://www.w3.org/2003/05/soap-envelope"">
					<soap:Header>
						<wsse:Security soap:mustUnderstand=""true""
								xmlns:wsse=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd""
								xmlns:wsu=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"">
							<wsse:UsernameToken wsu:Id=""";
			soapStr += userNameToken;
			soapStr += @""">
							<wsse:Username>" + UserName + @"</wsse:Username>
							<wsse:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"">" + Password + @"</wsse:Password>
							<wsse:Nonce EncodingType=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"">" + nonce + @"</wsse:Nonce>
							<wsu:Created>" + createdStr + @"</wsu:Created>
							</wsse:UsernameToken>
						</wsse:Security>
					</soap:Header>
					<soap:Body>
					<{0}>
						{1}
					</{0}>
					</soap:Body>
				</soap:Envelope>";
		}
		else if (WSSPasswordType == "None")
		{
			soapStr =
				@"<?xml version=""1.0"" encoding=""utf-8""?>
				<soap:Envelope xmlns:soap=""http://www.w3.org/2003/05/soap-envelope"">
					<soap:Body>
					<{0}>
						{1}
					</{0}>
					</soap:Body>
				</soap:Envelope>";
		}            

		HttpWebRequest req = (HttpWebRequest)WebRequest.Create(Url);
		req.Headers.Add("SOAPAction", SOAPAction);
		req.ContentType = "application/soap+xml;charset=\"utf-8\"";
		req.Accept = "application/soap+xml";
		req.Method = "POST";

		if (WSSPasswordType == "None")
		{
			NetworkCredential netCredential = new NetworkCredential(UserName, Password);
			byte[] credentialBuffer = new UTF8Encoding().GetBytes(UserName + ":" + Password);
			string auth = Convert.ToBase64String(credentialBuffer);
			req.Headers.Add("Authorization", "Basic " + auth);
		}

		ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(
			delegate (
			object sender,
			X509Certificate certificate,
			X509Chain chain,
			SslPolicyErrors sslPolicyErrors)
		{
			return true;
		});

		using (Stream stm = req.GetRequestStream())
		{
			string postValues = "";
			foreach (var param in Params)
			{
				if (encode)
				{
					postValues += string.Format("<{0}>{1}</{0}>", HttpUtility.UrlEncode(param.Key), HttpUtility.UrlEncode(param.Value));
				}
				else
				{
					postValues += string.Format("<{0}>{1}</{0}>", param.Key, param.Value);
				}
			}

			soapStr = string.Format(soapStr, MethodName, postValues);
			using (StreamWriter stmw = new StreamWriter(stm))
			{
				stmw.Write(soapStr);
			}
		}

		using (StreamReader responseReader = new StreamReader(req.GetResponse().GetResponseStream()))
		{
			string result = responseReader.ReadToEnd();
			ResultXML = XDocument.Parse(result);
			ResultString = result;
		}
	}
}

Inputs, Variables

Notice the soapStr variable above. The text/format of this variable may depend on your actual web service. So while the above sample code will very likely work for your setup, it may be a good idea to build your SOAP request using SOAP UI first and then grab the XML that SOAP UI generates.

The above code allows for situations where the value of WSSPasswordType is either “PasswordText” or “None”. This value will be set when calling the web service (see below). Again, the value you choose will depend on how your web service has been set up.

SOAP UI also exposes the “WSS-Password Type” variable in the request properties section. So don’t forget to set that correctly if you’re building your SOAP request string using SOAP UI.

The MethodName and SOAPAction inputs are related but again, their exact values depend on how your web service has been set up. As the name implies, MethodName is the name of the remote method you want to call. SOAPAction usually includes the method name at the end. But I think the best way to grab the exact value for SOAPAction is by looking into the WSDL of your web service (or by looking at the RAW request generated by SOAP UI after a successful call).

For example, the web service I was playing with while writing this article exposed the following:

  • MethodName: sapUserCreate
  • SOAPAction: SAPUserControl_Provider_WS_sapUserControl_Binder_sapUserCreate

…I was interfacing SharePoint with SAP modules using K2 and C#. Sweet stuff 🙂

The “How” Part: Invoking The Service

This part is simple. Just call the web service like this:

MyWebService mws = new MyWebService("webServiceUrl", "methodToCall", "webServiceUserName", "webServicePassword", "SOAPAction", "PasswordText");
mws.Params.Add("param1", "value_1");
mws.Params.Add("param2", "value_2");
mws.Invoke();

Add as many parameters as exposed/required by your web service.

Now you can view the result string or the result XML using:

string myResultString = mws.ResultString;
XDocument myResultXml = mws.ResultXML;

Hope this helps someone.

If you know a more elegant way to call SOAP based web services without adding a reference using C#, don’t hesitate to contribute by adding your comments below.

Found this article valuable? Want to show your appreciation? Here are some options:

  1. Spread the word! Use these buttons to share this link on your favorite social media sites.
  2. Help me share this on . . .

    • Facebook
    • Twitter
    • LinkedIn
    • Reddit
    • Tumblr
    • Pinterest
    • Pocket
    • Telegram
    • WhatsApp
    • Skype
  3. Sign up to join my audience and receive email notifications when I publish new content.
  4. Contribute by adding a comment using the comments section below.
  5. Follow me on Twitter, LinkedIn, and Facebook.

Related

Filed Under: Backend (Server-Side), C#, Programming Tagged With: CSharp, Programming

About Ehi Kioya

I am a Toronto-based Software Engineer. I run this website as part hobby and part business.

To share your thoughts or get help with any of my posts, please drop a comment at the appropriate link.

You can contact me using the form on this page. I'm also on Twitter, LinkedIn, and Facebook.

Reader Interactions

Comments

  1. win says

    March 24, 2018 at 4:26 am

    Soap call gives 500 (internal server error)
    how to do resolved it???

    Reply
  2. vighnesh kumar says

    June 6, 2020 at 6:41 am

    thank you so much, its help me a lot

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Primary Sidebar

23,737
Followers
Follow
30,000
Connections
Connect
14,568
Page Fans
Like
  • Recently   Popular   Posts   &   Pages
  • Actual Size Online Ruler Actual Size Online Ruler
    I created this page to measure your screen resolution and produce an online ruler of actual size. It's powered with JavaScript and HTML5.
  • How To Change A SharePoint List Or Library URL How To Change A SharePoint List Or Library URL
    All versions of the SharePoint user interface provide an option to change the title (or display name) of a list or library. Changing SharePoint library URL (or internal name), however, is not exactly very intuitive. We will discuss the process in this article.
  • WordPress Password Hash Generator WordPress Password Hash Generator
    With this WordPress Password Hash Generator, you can convert a password to its hash, and then set a new password directly in the database.
  • About
  • Contact

© 2022   ·   Ehi Kioya   ·   All Rights Reserved
Privacy Policy