C# custom SOAP header

While working on an SOAP service I needed to create a number of clients for different languages. This would normally not be that big of a challenge except that the SOAP service had custom headers for doing authentication. Because of the complexity in setting up Axis to use WS-Security the choice was made to do authentication by adding a few out of band SOAP header values. The first few implementations went fine but then I came to C# and had a problem.

The problem was that using the auto-generated code from the WSDL didn't provide any obvious way of adding the headers I needed to add. After a lot of digging I found an article on adding Support for Custom HTTP and SOAP Headers and that led me down the following path to a solution.

First off you need a custom header class that derives from SoapHeader. This class will contain the attributes that get sent in the SOAP header. Here is an example:

[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://model.ioncannon.net")]
[System.Xml.Serialization.XmlRootAttribute("securitytoken", Namespace = "http://model.ioncannon.net", IsNullable = false)]
public class CustomHeader : SoapHeader
{
  [System.Xml.Serialization.XmlTextAttribute()]
  public string securitytoken;
}

Next you will need an instance of the custom header in the generated service client. The variable name needs to match what gets stuck in something later so make it readable:

public CustomHeader creds;

The final step is to a reference to the custom header before each call that requires the custom header. This is probably the least obvious part. The first line in the following code is the tie between the custom header instance created above and the generated "echo" service call:

[System.Web.Services.Protocols.SoapHeaderAttribute("creds", Direction=System.Web.Services.Protocols.SoapHeaderDirection.InOut)]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("urn:echo", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
[return: System.Xml.Serialization.XmlElementAttribute("echoResponse", Namespace="http://model.ioncannon.net")]
public echoResponse echo([System.Xml.Serialization.XmlElementAttribute("echo", Namespace="http://model.ioncannon.net")] echo echo1) {
  object[] results = this.Invoke("echo", new object[] {echo1});
  return ((echoResponse)(results[0]));
}

Here is an example of calling the modified service:

CustomHeader mySoapHeader = new CustomHeader();
mySoapHeader.securitytoken = "testtoken";
 
TestService client = new TestService();
client.creds = mySoapHeader;
echo request = new echo();
request.valueToEcho = "test";
string echoValue = client.echo(request).@return;
Console.WriteLine(echoValue);

The only downside to doing this is that regenerating the code from an updated WSDL will wipe out the changes.

Leave a Reply

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