Click here to Skip to main content
Click here to Skip to main content

SOAP Header Extensions How-To

, 6 Aug 2007
Rate this:
Please Sign up or sign in to vote.
Passing non-standard information in SOAP headers

Introduction

Create a SoapExtension that allows you to insert XML into a SOAP header.

Background

I work for a very large company with diverse technology solutions. My shop is .NET, but we're consuming a web service from another group that uses WebSphere and Java for their web services. When I tried to consume this web service from my ASP.NET application, I ran into some issues, because the web service expected a username and password security token, passed in the SOAP header. I assumed (gulp) that it'd be a small matter of programming (SMOP) to insert well-formed XML into a SOAP header. That's mostly right, but there is a complete dearth of documentation on how to do this. I'm from the Internets, and I'm here to help, folks.

Using the code

I'm consuming a Java web service from a WebSphere server. I have no problems creating the web reference and consuming the service, but apparently it expects more than what .NET sends it. Here's the header that comes out of my ASP.Net application:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:tns="http://tempuri.org/" 
xmlns:types="http://tempuri.org/encodedTypes" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
.
. Body stuff here, with appropriate XML, passing parameters and values to 
  the web service...
.

Here's what the web service expects:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Header>
<Security>
   <UsernameToken>
      <Username>XXXXXXX</Username>
      <Password>XXXXXXXXXXXXXXXXXXXX</Password>
   </UsernameToken>
</Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
.
.Body stuff here, with appropriate XML, passing parameters and values to 
 the web service...

Let me be clear: this is an ASP.NET (C#) web form application, consuming a WebSphere web service. There is NO web method in the service for passing the username token. As I've illustrated above, the expectation is for this token (well-formed XML) to be inserted into the SOAP header. This is where the SoapExtension comes to the rescue.

First, you need to create an extension class, one that inherits SoapExtension. Something along the lines of:

public class myExtension : SoapExtension

By default, a SOAP extension has 5 overrides that you must provide: ChainStream, GetInitializer (2 of them), Initialize, and ProcessMessage. ProcessMessage and ChainStream are really the heart of the matter, and you'll see why in the code. ChainStream provides an override to access the XML stream. ProcessMessage provides an override for you to manipulate that stream. Inside ProcessMessage, you'll need to test for the 4 stages of a SoapMessage, namely .BeforeSerialize, .AfterSerialize, .BeforeDeserialize, and .AfterDeserialize. We're interested in what's being sent from the client to the server, so the SoapMessageStage.AfterSerialize test is where we get the XML from cache, convert it to a string, do our manipulations, convert the string back out to a stream, and send it on it's merry way. Once this class is built, you need to perform one more very important step - make that extension available to your application. I have a web application, so I put this into the web.config file:

<webServices>
    <soapExtensionTypes>
        <add type="bberry1.myExtension,bberry1" priority="1" group="Low"/>
    </soapExtensionTypes>
</webServices>
</system.web>

Note that my namespace is bberry1, and the extension class is the brilliantly named "myExtension." Once that's done, and you've rebuilt your project, you call your webservice, your overrides kick in, and you get what you want. Next, the class is constructed. I've placed all the code in a file named "customfilter.cs" in my project.

I mentioned several overrides above, and pointed out that ChainStream and ProcessMessage were the crux of the issue. I've got a few declarations to make for this class first:

public class myExtension : SoapExtension
{
   public bool outgoing = true;
   public bool incoming = false;
   private Stream outputStream;
   public Stream oldStream;
   public Stream newStream;

Next, I build the ChainStream override:

public override Stream ChainStream(Stream stream)
{
// save a copy of the stream, create a new one for manipulating.
   this.outputStream = stream;
   oldStream = stream;
   newStream = new MemoryStream();
   return newStream;
}

This makes the stream available to us for use in ProcessMessage. ProcessMessage is where we see what phase of the whole SOAP stream we're in, and process accordingly. I can try to explain the gyrations here, but I think the code speaks for itself. One thing that bears explanation, however, is that I convert the outgoing stream to a string, add my stuff, then convert the string back out to the stream. This is all done in SoapMessageStage.AfterSerialize. I still need for that stream to go back out to the web service, so I need to process that stream out down in SoapMessageStage.BeforeDeserialize.

The process of converting the XML cache to string gave me quite the headache, as there was almost NO documentation on the Internets on how it's done (I'm a complete SOAP/XML newbie, mind you). As it turns out, it's pretty easy, and is done like this:

public string getXMLFromCache()
{
   newStream.Position = 0; // start at the beginning!
   string strSOAPresponse = ExtractFromStream(newStream);
   return strSOAPresponse;
}

Here's the ExtractFromStream function:

private String ExtractFromStream(Stream target)
{
   if (target != null)
   return (new StreamReader(target)).ReadToEnd();
   return "";
}

Here's the ProcessMessage override in it's entirety:

public override void ProcessMessage(SoapMessage message)
{
   StreamReader readStr;
   StreamWriter writeStr;
   string soapMsg1;
   XmlDocument xDoc = new XmlDocument();
   // a SOAP message has 4 stages. We're interested in .AfterSerialize
   switch (message.Stage)
   {
      case SoapMessageStage.BeforeSerialize:
      break;
      case SoapMessageStage.AfterSerialize:
      {
      // Get the SOAP body as a string, so we can manipulate...
      String soapBodyString = getXMLFromCache();
      // Strip off the old header stuff before the message body
      // I'm not completely sure, but the soap:encodingStyle might be
      // unique to the WebSphere environment. Dunno.
      String BodString = 
"<soap:Body soap:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">";
      int pos1 = soapBodyString.IndexOf(BodString) + BodString.Length;
      int pos2 = soapBodyString.Length - pos1;
      soapBodyString = soapBodyString.Substring(pos1, pos2);
      soapBodyString = "<soap:Body>" + soapBodyString;
      // Create the SOAP Message 
      // It's comprised of a <soap:Element> that's enclosed in <soap:Body>. 
      // Pack the XML document inside the <soap:Body> element 
      String xmlVersionString = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
      String soapEnvelopeBeginString = 
      "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" 
          xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" 
          xmlns:tns=\"http://tempuri.org/\" 
          xmlns:types=\"http://tempuri.org/encodedTypes\" 
          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 
          xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">";
      String soapEnvHeaderString = 
          "<soap:Header><Security><UsernameToken><Username>";
      String soapEnvHeaderString2 = "</Username><Password>";
      String soapEnvHeaderString3 = 
          "</Password></UsernameToken></Security></soap:Header>";
      Stream appOutputStream = new MemoryStream();
      StreamWriter soapMessageWriter = new StreamWriter(appOutputStream);
      soapMessageWriter.Write(xmlVersionString);
      soapMessageWriter.Write(soapEnvelopeBeginString);
      // The heavy-handed part - forcing the right headers AND the uname/pw :)
      soapMessageWriter.Write(soapEnvHeaderString);
      soapMessageWriter.Write("usernamestring");
      soapMessageWriter.Write(soapEnvHeaderString2);
      soapMessageWriter.Write("passwordstring");
      soapMessageWriter.Write(soapEnvHeaderString3);
      // End clubbing of baby seals
      // Add the soapBodyString back in - it's got all the closing 
      // XML we need.
      soapMessageWriter.Write(soapBodyString);
      // write it all out.
      soapMessageWriter.Flush();
      appOutputStream.Flush();
      appOutputStream.Position = 0;
      StreamReader reader = new StreamReader(appOutputStream);
      StreamWriter writer = new StreamWriter(this.outputStream);
      writer.Write(reader.ReadToEnd());
      writer.Flush();
      appOutputStream.Close();
      this.outgoing = false;
      this.incoming = true;
      break;
    }
   case SoapMessageStage.BeforeDeserialize:
   {
      // Make the output available for the client to parse...
      readStr = new StreamReader(oldStream);
      writeStr = new StreamWriter(newStream);
      soapMsg1 = readStr.ReadToEnd();
      xDoc.LoadXml(soapMsg1);
      soapMsg1 = xDoc.InnerXml;
      writeStr.Write(soapMsg1);
      writeStr.Flush();
      newStream.Position = 0;
      break;
   }
   case SoapMessageStage.AfterDeserialize:
   break;
   default:
   throw new Exception("invalid stage!");
   }
 }
}

Points of Interest

What's good about this little exercise is that I found out how completely I can control the information flowing back and forth between my client and the web service. It's really nice to know that Microsoft provides the SoapExtension, but it would've been nicer to have a how-to like this one as part of the documentation.

History

  • Aug 6th, 2007: Initial Release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Jim Roth
Architect Epsilon
United States United States
Technology Director at Epsilon, a leader in marketing technology.

Comments and Discussions

 
QuestionExactly what I needed. I made a small improvement to the code to avoid the tricky string manipulation. Pinmemberblackstorm17-Dec-13 22:15 
AnswerRe: Exactly what I needed. I made a small improvement to the code to avoid the tricky string manipulation. PinmemberJim Roth12-Feb-14 9:49 
GeneralMy vote of 1 Pinmemberjohna@ocsx.com7-Oct-10 13:39 
GeneralRe: My vote of 1 PinmemberJim Roth8-Nov-10 4:45 
GeneralHaving Problems Pinmemberlbloom30-Oct-08 7:45 
GeneralRe: Having Problems PinmemberJim Roth30-Oct-08 8:39 
QuestionMax Length for SOAP Message PinmemberGreg Estes16-Sep-08 5:00 
AnswerRe: Max Length for SOAP Message PinmemberJim Roth16-Sep-08 5:45 
GeneralRe: Max Length for SOAP Message PinmemberGreg Estes16-Sep-08 6:01 
GeneralRe: Max Length for SOAP Message PinmemberJim Roth16-Sep-08 6:22 
GeneralRe: Max Length for SOAP Message PinmemberGreg Estes17-Sep-08 3:38 
GeneralRe: Max Length for SOAP Message PinmemberJim Roth17-Sep-08 3:55 
GeneralRe: Max Length for SOAP Message PinmemberGreg Estes17-Sep-08 4:14 
GeneralRe: Max Length for SOAP Message PinmemberJim Roth17-Sep-08 4:25 
GeneralRe: Max Length for SOAP Message PinmemberGreg Estes17-Sep-08 5:11 
GeneralRe: Max Length for SOAP Message PinmemberGreg Estes17-Sep-08 10:39 
GeneralRe: Max Length for SOAP Message PinmemberJim Roth17-Sep-08 11:03 
GeneralRe: Max Length for SOAP Message PinmemberGreg Estes17-Sep-08 12:50 
GeneralInteresting Pinmember Dr.Luiji 24-Apr-08 12:09 
QuestionWhat would I do without you.... Pinmemberdeanders19-Mar-08 12:34 
GeneralRe: What would I do without you.... Pinmemberdeanders19-Mar-08 13:08 
GeneralRe: What would I do without you.... PinmemberJim Roth20-Mar-08 3:36 
Questionpass username and password Pinmemberrzubi25-Jan-08 9:01 
AnswerRe: pass username and password Pinmemberdeanders4-Apr-08 13:07 
GeneralNice article. I did similar things, but not use soapextension Pinmembercoderflash13-Dec-07 5:25 
GeneralFollow Up on WSE 3.0 v.s. extensions... Pinmemberdtfhome10-Nov-07 3:38 
GeneralRe: Follow Up on WSE 3.0 v.s. extensions... PinmemberJim Roth12-Nov-07 5:13 
GeneralWay not use WSE 3.0 PinmemberDanielWehrle16-Aug-07 0:18 
GeneralRe: Way not use WSE 3.0 PinmemberJim Roth16-Aug-07 1:24 
QuestionCan you provide the GetInitializer functions? PinmemberSupermanDT14-Aug-07 7:06 
AnswerRe: Can you provide the GetInitializer functions? PinmemberJim Roth14-Aug-07 7:11 
GeneralRe: Can you provide the GetInitializer functions? PinmemberSupermanDT14-Aug-07 7:17 
GeneralRe: Can you provide the GetInitializer functions? PinmemberJim Roth14-Aug-07 7:45 
QuestionRe: Can you provide the GetInitializer functions? PinmemberBill Schroeder14-Aug-07 10:52 
AnswerRe: Can you provide the GetInitializer functions? PinmemberJim Roth14-Aug-07 10:56 
GeneralRe: Can you provide the GetInitializer functions? PinmemberSupermanDT14-Aug-07 12:01 
GeneralRe: Can you provide the GetInitializer functions? PinmemberJim Roth14-Aug-07 12:04 
GeneralRe: Can you provide the GetInitializer functions? PinmemberBill Schroeder15-Aug-07 3:26 
GeneralRe: Can you provide the GetInitializer functions? PinmemberJim Roth15-Aug-07 3:31 
QuestionWhy you need SOAP Extension for modifying Header? Pinmemberchak200710-Aug-07 8:16 
AnswerRe: Why you need SOAP Extension for modifying Header? PinmemberJim Roth10-Aug-07 9:14 
AnswerRe: Why you need SOAP Extension for modifying Header? PinmemberJim Roth13-Aug-07 4:12 
GeneralRe: Why you need SOAP Extension for modifying Header? PinmemberChris Schaller19-Aug-07 11:57 
GeneralRe: Why you need SOAP Extension for modifying Header? PinmemberJim Roth20-Aug-07 3:02 
GeneralRe: Why you need SOAP Extension for modifying Header? PinmemberChris Schaller20-Aug-07 10:32 
GeneralRe: Why you need SOAP Extension for modifying Header? PinmemberJim Roth20-Aug-07 10:59 
GeneralRe: Why you need SOAP Extension for modifying Header? Pinmemberajay_sp00225-Feb-11 1:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 6 Aug 2007
Article Copyright 2007 by Jim Roth
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid