By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
432,027 Members | 1,088 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 432,027 IT Pros & Developers. It's quick & easy.

Inserting a SOAP Header in a client request

P: n/a
I have a C#/.Net 1.1 client talking to a Java based web service. I need to
insert a soap header on the client side which is expected on the server side.
Currently, the Java ws provider, Axis, does not support automatic wsdl
generation of custom headers so the wsdl has no information regarding the
required header.

I've read through a lot of material and managed to get a workable solution
but it is far from ideal.

I created a new AuthHeader:SoapHeader class and updated the client ws proxy
to make use of the new header. This worked but I want to avoid having to
modify tool generated code. I don't want to touch the .Net wsdl.exe
generated ws proxy.

One other solution may be to modify the stream via my own SoapExtension
class. Problem is that requires working at a low level on the stream. Why
should I have to do that when .Net provides a rich SOAP API?

I would think I should be able to simply insert my own SoapHeader into the
SoapMessage via SoapExtension.ProcessMessage(). I have tried this approach
without success. I get a System.InvalidOperationException upon attempted
serialization of the SoapMessage. The error is, "The type
SimpleWSClient.AuthHeader was not expected. Use the XmlInclude or SoapInclude
attribute to specify types that are not known statically." It seems like
this approach should work. I assumed that a SoapHeader object would already
know how to serialize itself if it contains only basic types.

My SoapHeader and SoapExtension classes below:

using System;
using System.Xml.Serialization;
using System.Web.Services.Protocols;

namespace SimpleWSClient
{
public class AuthHeader : SoapHeader
{
private string userName;
private string password;

public AuthHeader()
{
}

public AuthHeader(string user, string pwd)
{
UserName = user;
Password = pwd;
}

public string UserName
{
get { return userName; }
set { userName = value; }
}

public string Password
{
get { return password; }
set { password = value; }
}
}

}

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml;
using System.Xml.Serialization;

namespace SimpleWSClient
{
/// <summary>
/// SOAP Extension that checks the SOAP Header for a username and password
/// and does the necessary authentication and authorization.
/// </summary>
public class UsernamePasswordSoapExtension : SoapExtension
{

public override object GetInitializer( Type serviceType )
{
return null;
}

public override object GetInitializer( LogicalMethodInfo methodInfo,
SoapExtensionAttribute attribute )
{
return null;
}

public override void Initialize( object initializer )
{
}

public override void ProcessMessage( SoapMessage message )
{
if (message.Stage == SoapMessageStage.BeforeSerialize)
{
SecurityContext context = SecurityContext.getInstance();
AuthHeader soapHeader = new AuthHeader(context.UserName,
context.Password);
SoapClientMessage scm = (SoapClientMessage)message;
SoapHeaderCollection headers = scm.Headers;
headers.Add(soapHeader);
}
}

}
}
This last approach is what I am after. It would allow me to insert a custom
SoapHeader using a high-level Soap API without having to inject custom code
into a tool generated ws proxy.

Is it possible to make this last approach work or am I forced to work with
the first two options?

Thanks.
Nov 23 '05 #1
Share this Question
Share on Google+
6 Replies


P: n/a
Hello john,
I dont see the difference in what you call the second option and the
third option. What you seem to be missing is overriding the chain stream
and writing to the stream (which I gather you dont wish to do). The problem
with your code is .. yes you're using the soap object model to add the header
but you've failed to modify the stream by writing that information back into
the stream. Also have you considered using WSE? and also are you sure the
java service is not using WS-Security? If it is, you would need to send standard
username tokens as opposed to a custom auth header

HTH
Regards,
Dilip Krishnan
MCAD, MCSD.net
dkrishnan at geniant dot com
http://www.geniant.com
I have a C#/.Net 1.1 client talking to a Java based web service. I
need to
insert a soap header on the client side which is expected on the
server side.
Currently, the Java ws provider, Axis, does not support automatic
wsdl
generation of custom headers so the wsdl has no information regarding
the
required header.

I've read through a lot of material and managed to get a workable
solution but it is far from ideal.

I created a new AuthHeader:SoapHeader class and updated the client ws
proxy to make use of the new header. This worked but I want to avoid
having to modify tool generated code. I don't want to touch the .Net
wsdl.exe generated ws proxy.

One other solution may be to modify the stream via my own
SoapExtension class. Problem is that requires working at a low level
on the stream. Why should I have to do that when .Net provides a rich
SOAP API?

I would think I should be able to simply insert my own SoapHeader into
the SoapMessage via SoapExtension.ProcessMessage(). I have tried this
approach without success. I get a System.InvalidOperationException
upon attempted serialization of the SoapMessage. The error is, "The
type SimpleWSClient.AuthHeader was not expected. Use the XmlInclude or
SoapInclude attribute to specify types that are not known statically."
It seems like this approach should work. I assumed that a SoapHeader
object would already know how to serialize itself if it contains only
basic types.

My SoapHeader and SoapExtension classes below:

using System;
using System.Xml.Serialization;
using System.Web.Services.Protocols;
namespace SimpleWSClient
{
public class AuthHeader : SoapHeader
{
private string userName;
private string password;
public AuthHeader()
{
}
public AuthHeader(string user, string pwd)
{
UserName = user;
Password = pwd;
}
public string UserName
{
get { return userName; }
set { userName = value; }
}
public string Password
{
get { return password; }
set { password = value; }
}
}
}

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml;
using System.Xml.Serialization;
namespace SimpleWSClient
{
/// <summary>
/// SOAP Extension that checks the SOAP Header for a username and
password
/// and does the necessary authentication and authorization.
/// </summary>
public class UsernamePasswordSoapExtension : SoapExtension
{
public override object GetInitializer( Type serviceType )
{
return null;
}
public override object GetInitializer( LogicalMethodInfo methodInfo,
SoapExtensionAttribute attribute )
{
return null;
}
public override void Initialize( object initializer )
{
}
public override void ProcessMessage( SoapMessage message )
{
if (message.Stage == SoapMessageStage.BeforeSerialize)
{
SecurityContext context = SecurityContext.getInstance();
AuthHeader soapHeader = new AuthHeader(context.UserName,
context.Password);
SoapClientMessage scm = (SoapClientMessage)message;
SoapHeaderCollection headers = scm.Headers;
headers.Add(soapHeader);
}
}
}
}
This last approach is what I am after. It would allow me to insert a
custom SoapHeader using a high-level Soap API without having to inject
custom code into a tool generated ws proxy.

Is it possible to make this last approach work or am I forced to work
with the first two options?

Thanks.

Nov 23 '05 #2

P: n/a
Sorry if my original post was not clear. Let me clarify the options I was
exploring:

Option 1: custom SoapHeader and modify the ws client proxy by hand to
reference the new SoapHeader..

Option 2: custom SoapExtension and modify the stream by hand via
ChainStream().

Option 3: custom Soap Extension and SoapHeader and modify the SoapMessage
via ProcessMessage().

So, it sounds like option 3 is not really an option. Option 2 means working
with a byte stream instead of a Soap API. ChainStream() is the only place we
can modify the message before serialization? I guess I'm still not quite
clear on the distinction between ChainStream() and ProcessMessage().

Given that this is all in the context of a web service request, I shouldn't
have to work at the byte level to add a soap header to a soap message. I
imagine the Stream could somehow be converted to a SoapMessage before adding
the soap header but that would be inefficient given that it will be converted
into a SoapMessage later in the call chain.

WSE is something I will explore but for the time being this is what I am
working with. I need to research Axis 1.1 support of WS-Security and compare
it to .Net.
The server side of this solution already exists and works for a different
client implementation.

Clean insertion of a custom soap header on the client side should be
relatively painless. This is how it's done in Axis (Java):

// setup authorization header
SOAPHeaderElement authHeader = new SOAPHeaderElement( "",
"AuthHeader" );
MessageElement usernameElement = ( MessageElement )
authHeader.addChildElement( "UserName" );
MessageElement passwordElement = ( MessageElement )
authHeader.addChildElement( "Password" );
usernameElement.setObjectValue("jdoe");
passwordElement.setObjectValue("password");
call.addHeader( authHeader );

John

"Dilip Krishnan" wrote:
Hello john,
I dont see the difference in what you call the second option and the
third option. What you seem to be missing is overriding the chain stream
and writing to the stream (which I gather you dont wish to do). The problem
with your code is .. yes you're using the soap object model to add the header
but you've failed to modify the stream by writing that information back into
the stream. Also have you considered using WSE? and also are you sure the
java service is not using WS-Security? If it is, you would need to send standard
username tokens as opposed to a custom auth header

HTH
Regards,
Dilip Krishnan
MCAD, MCSD.net
dkrishnan at geniant dot com
http://www.geniant.com


Nov 23 '05 #3

P: n/a
I reviewed the article, "Altering the SOAP Message Using SOAP Extensions". I
see that update of the soap message requires overrides in both ChainStream()
and ProcessMessage().

What I don't understand is why I'm not able to update the soap message via
the SoapMessage API. ProcessMessage() is passed a SoapMessage object but it
can't be directly updated, I am forced to update a byte stream instead.
SoapExtension inherits from System.Object so I'm puzzled why updates are only
allowed via a byte stream.

Are there any other options?
Nov 23 '05 #4

P: n/a
Hello john,
You really dont have to play around with bits and bytes. Every Soap based
Object has a LoadXml and GetXml that you can use in conjunction with a XmlTextReader/Writer.
So all you need to do is write to the chained stream using an xml writer.
But u do have to chain the stream to be able to alter the stream
HTH
Regards,
Dilip Krishnan
MCAD, MCSD.net
dkrishnan at geniant dot com
http://www.geniant.com
Sorry if my original post was not clear. Let me clarify the options I
was exploring:

Option 1: custom SoapHeader and modify the ws client proxy by hand to
reference the new SoapHeader..

Option 2: custom SoapExtension and modify the stream by hand via
ChainStream().

Option 3: custom Soap Extension and SoapHeader and modify the
SoapMessage via ProcessMessage().

So, it sounds like option 3 is not really an option. Option 2 means
working with a byte stream instead of a Soap API. ChainStream() is
the only place we can modify the message before serialization? I
guess I'm still not quite clear on the distinction between
ChainStream() and ProcessMessage().

Given that this is all in the context of a web service request, I
shouldn't have to work at the byte level to add a soap header to a
soap message. I imagine the Stream could somehow be converted to a
SoapMessage before adding the soap header but that would be
inefficient given that it will be converted into a SoapMessage later
in the call chain.

WSE is something I will explore but for the time being this is what I
am
working with. I need to research Axis 1.1 support of WS-Security and
compare
it to .Net.
The server side of this solution already exists and works for a
different
client implementation.
Clean insertion of a custom soap header on the client side should be
relatively painless. This is how it's done in Axis (Java):

// setup authorization header
SOAPHeaderElement authHeader = new SOAPHeaderElement( "",
"AuthHeader" );
MessageElement usernameElement = ( MessageElement )
authHeader.addChildElement( "UserName" );
MessageElement passwordElement = ( MessageElement )
authHeader.addChildElement( "Password" );
usernameElement.setObjectValue("jdoe");
passwordElement.setObjectValue("password");
call.addHeader( authHeader );
John

"Dilip Krishnan" wrote:
Hello john,
I dont see the difference in what you call the second option and the
third option. What you seem to be missing is overriding the chain
stream
and writing to the stream (which I gather you dont wish to do). The
problem
with your code is .. yes you're using the soap object model to add
the header
but you've failed to modify the stream by writing that information
back into
the stream. Also have you considered using WSE? and also are you sure
the
java service is not using WS-Security? If it is, you would need to
send standard
username tokens as opposed to a custom auth header
HTH
Regards,
Dilip Krishnan
MCAD, MCSD.net
dkrishnan at geniant dot com
http://www.geniant.com

Nov 23 '05 #5

P: n/a
So, my SoapHeader object has a GetXml() implementation that is not public?

Can you point to some examples that demonstrate this?

How do I write the header to the stream in the correct place? I understand
that the SoapExtension has a priority and group associated it and that the
priority is relative. But relative to what? I am using an App.config file
to declare the soap extension. Assuming my SoapHeader already knows how to
write the correct XML for itself how do I ensure that my SoapHeader is
written inside the Envelope but before the Body?

"Dilip Krishnan" wrote:
Hello john,
You really dont have to play around with bits and bytes. Every Soap based
Object has a LoadXml and GetXml that you can use in conjunction with a XmlTextReader/Writer.
So all you need to do is write to the chained stream using an xml writer.
But u do have to chain the stream to be able to alter the stream
HTH
Regards,
Dilip Krishnan
MCAD, MCSD.net
dkrishnan at geniant dot com
http://www.geniant.com


Nov 23 '05 #6

P: n/a
Hello john,
Something like this...

private void AddViaElementToHeader(SoapMessage message ) //message that
comes in the process message
{
ISoapFormatter formatter = WebServicesConfiguration.MessagingConfiguration.As mxPlainFormatter;
SoapEnvelope env = null;
if(message is SoapServerMessage)
{
try
{
if (!message.ContentType.StartsWith(WebServicesConfig uration.MessagingConfiguration.AsmxPlainFormatter. ContentType))
//This is a hack
{
formatter = WebServicesConfiguration.MessagingConfiguration.As mxAttachmentFormatter;
}

env = formatter.Deserialize(_originalStream); //message from stream
}
catch(Exception)
{
HttpContext.Current.Response.StatusCode = 400;
HttpContext.Current.Response.Buffer = false;
HttpContext.Current.Response.OutputStream.Flush();
HttpContext.Current.ApplicationInstance.CompleteRe quest();
return;
}
XmlElement header = null;
header = env.CreateElement("yrHeader", YourNamespaceURI);
header .InnerText = this.ServiceUrl;
env.Header.AppendChild(header);
}
env.Save(_replacedStream); //Stream that you chained
if (this._replacedStream.CanSeek)
{
this._replacedStream.Position = 0;
}

}
HTH
Regards,
Dilip Krishnan
MCAD, MCSD.net
dkrishnan at geniant dot com
http://www.geniant.com
So, my SoapHeader object has a GetXml() implementation that is not
public?

Can you point to some examples that demonstrate this?

How do I write the header to the stream in the correct place? I
understand that the SoapExtension has a priority and group associated
it and that the priority is relative. But relative to what? I am
using an App.config file to declare the soap extension. Assuming my
SoapHeader already knows how to write the correct XML for itself how
do I ensure that my SoapHeader is written inside the Envelope but
before the Body?

"Dilip Krishnan" wrote:
Hello john,
You really dont have to play around with bits and bytes. Every Soap
based
Object has a LoadXml and GetXml that you can use in conjunction with
a XmlTextReader/Writer.
So all you need to do is write to the chained stream using an xml
writer.
But u do have to chain the stream to be able to alter the stream
HTH
Regards,
Dilip Krishnan
MCAD, MCSD.net
dkrishnan at geniant dot com
http://www.geniant.com

Nov 23 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.