Adding custom headers to SOAP request in UWP

How to create and add custom headers to SOAP requests in a Universal Windows Platform app.

Posted by Umut Canbolat on April 20, 2018 · 6 mins read

Last week I needed to make a SOAP service call at work. The customer provided us WSDL definitions. I was supposed to add the service references in Visual Studio and make requests from UWP app. After adding the service references, I noticed that the service proxy didn’t include header objects. So I created custom MessageHeader class and appended it to the SOAP request message. I didn’t have access to the server-side. So this post is only about making SOAP requests on client-side.

In these WSDL’s, body objects were defined. The problem was that I was not able to set header nodes because they were not defined. I added them to request message by extending MessageHeader class. I also needed to add XML namespace to the header tag with a custom prefix. I also covered it in the following example.

Creating Custom MessageHeader Class

To add custom headers, we need to create a class which extends MessageHeader and override some fields in it. Here I will add 2 different header tags. One with custom XML namespace prefix and the other one without. The example will go like following:

public class CustomHeader : MessageHeader
{
    private const string HeaderName = "p:someTag";
    private const string HeaderNamespace = "";
    public override string Name => HeaderName;
    public override string Namespace => HeaderNamespace;
    public string headerString;
 
    public CustomHeader()
    {
        this.headerString = @"<innerTag>content</innerTag>";
    }
 
    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        writer.WriteXmlnsAttribute("p", "http://www.name.space.url/");
        var r = XmlReader.Create(new StringReader(headerString));
        r.MoveToContent();
        writer.WriteNode(r, false);
    }
}
 
public class SecurityHeader : MessageHeader
{
    private const string HeaderName = "Security";
    private const string HeaderNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
    public override string Name => HeaderName;
    public override string Namespace => HeaderNamespace;
    public string headerString;
 
    public SecurityHeader(string username, string password)
    {
        this.headerString = @"<UsernameToken xmlns=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd""> 
        <Username>" + username + @"</Username> 
        <Password>" + password + @"</Password> </UsernameToken>";
    }
 
    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        var r = XmlReader.Create(new StringReader(headerString));
        r.MoveToContent();
        writer.WriteNode(r, false);
    }
}

Adding Message Headers to the SOAP Request

Before calling the web service, we can use our custom header classes like this:

var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
var svc = new ServiceClient(binding, new EndpointAddress("https://service.endpoint.url"));
 
try
{
    using (var scope = new OperationContextScope(svc.InnerChannel))
    {
        var cus = new CustomHeader();
        OperationContext.Current.OutgoingMessageHeaders.Add(cus);
        var sec = new SecurityHeader("USER", "PASS");
        OperationContext.Current.OutgoingMessageHeaders.Add(sec);
 
        return await svc.retrieveResponseAsync(req); ;
    }
}
catch (Exception ex)
{
     // log error
     return null;
}
finally
{
     await svc.CloseAsync();
}

The result request

At the end, the SOAP request we send will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Header>
      <p:someTag xmlns:p="http://www.name.space.url/">
         <innerTag>content</innerTag>
      </p:someTag>
      <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
         <UsernameToken>
            <Username>USER</Username>
            <Password>PASS</Password>
         </UsernameToken>
      </Security>
   </s:Header>
   <s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <!-- body content -->
   </s:Body>
</s:Envelope>

References