Wcf supports two WS-Addressing versions – the August 2004 draft and the actual standard v 1.0. There is another wsa version which is used by some soap stacks (who said wse 2?) – the march 2004 draft. To turn this fact to a more practical problem, you might need to write a wcf client to a (non Wcf) service which expects a request like this:
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing">
<Header>
<wsa:Action>http://myAction</wsa:Action>
<wsa:MessageID>uuid:5024616c-d0r2-56h3-bjj7-ab10p89eee63</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://server/Calc</wsa:To>
...
<Header>
<wsa:Action>http://myAction</wsa:Action>
<wsa:MessageID>uuid:5024616c-d0r2-56h3-bjj7-ab10p89eee63</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://server/Calc</wsa:To>
...
(note the bold wsa version – wcf cannot emit it).
One straight forward way to do this with wcf is to push these headers to the soap using a message inspector. But what if you also need to sign the wsa headers with a digital signature?In this case you need to write a custom behavior which push the wsa headers to the IncomingSignatureParts property. The whole code looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Security;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using MyApplication.ServiceReference1;
namespace MyApplication
{
public class SignMessageHeaderBehavior : Attribute, IEndpointBehavior
{
string action;
List<XmlQualifiedName> headers;
public SignMessageHeaderBehavior(List<XmlQualifiedName> headers, string action)
{
this.headers = headers;
this.action = action;
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
var requirements = bindingParameters.Find<ChannelProtectionRequirements>();
foreach (var h in headers)
{
var part = new MessagePartSpecification(h);
requirements.IncomingSignatureParts.AddParts(part, action);
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new AddHeaderMessageInspector());
}
}
[DataContract(Namespace="http://schemas.xmlsoap.org/ws/2004/03/addressing")]
public class ReplyToHeader
{
[DataMember]
public string Address { get; set; }
}
public class AddHeaderMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader("Action", "http://schemas.xmlsoap.org/ws/2004/03/addressing", "http://myAction"));
request.Headers.Add(MessageHeader.CreateHeader("MessageID", "http://schemas.xmlsoap.org/ws/2004/03/addressing", "uuid:5024616c-d0r2-56h3-bjj7-ab10p89eee63"));
request.Headers.Add(MessageHeader.CreateHeader("ReplyTo", "http://schemas.xmlsoap.org/ws/2004/03/addressing", new ReplyToHeader() { Address = "http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous" }));
request.Headers.Add(MessageHeader.CreateHeader("To", "http://schemas.xmlsoap.org/ws/2004/03/addressing", "http://server/Calc"));
return request;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
}
class Program
{
static void Main(string[] args)
{
var c = new SimpleServiceSoapClient();
var headers = new List<XmlQualifiedName>();
headers.Add(new XmlQualifiedName("Action", "http://schemas.xmlsoap.org/ws/2004/03/addressing"));
headers.Add(new XmlQualifiedName("MessageID", "http://schemas.xmlsoap.org/ws/2004/03/addressing"));
headers.Add(new XmlQualifiedName("ReplyTo", "http://schemas.xmlsoap.org/ws/2004/03/addressing"));
headers.Add(new XmlQualifiedName("To", "http://schemas.xmlsoap.org/ws/2004/03/addressing"));
c.Endpoint.Behaviors.Add(
new SignMessageHeaderBehavior(headers, "http://tempuri.org/EchoString"));
c.EchoString("123");
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Security;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using MyApplication.ServiceReference1;
namespace MyApplication
{
public class SignMessageHeaderBehavior : Attribute, IEndpointBehavior
{
string action;
List<XmlQualifiedName> headers;
public SignMessageHeaderBehavior(List<XmlQualifiedName> headers, string action)
{
this.headers = headers;
this.action = action;
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
var requirements = bindingParameters.Find<ChannelProtectionRequirements>();
foreach (var h in headers)
{
var part = new MessagePartSpecification(h);
requirements.IncomingSignatureParts.AddParts(part, action);
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new AddHeaderMessageInspector());
}
}
[DataContract(Namespace="http://schemas.xmlsoap.org/ws/2004/03/addressing")]
public class ReplyToHeader
{
[DataMember]
public string Address { get; set; }
}
public class AddHeaderMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader("Action", "http://schemas.xmlsoap.org/ws/2004/03/addressing", "http://myAction"));
request.Headers.Add(MessageHeader.CreateHeader("MessageID", "http://schemas.xmlsoap.org/ws/2004/03/addressing", "uuid:5024616c-d0r2-56h3-bjj7-ab10p89eee63"));
request.Headers.Add(MessageHeader.CreateHeader("ReplyTo", "http://schemas.xmlsoap.org/ws/2004/03/addressing", new ReplyToHeader() { Address = "http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous" }));
request.Headers.Add(MessageHeader.CreateHeader("To", "http://schemas.xmlsoap.org/ws/2004/03/addressing", "http://server/Calc"));
return request;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
}
class Program
{
static void Main(string[] args)
{
var c = new SimpleServiceSoapClient();
var headers = new List<XmlQualifiedName>();
headers.Add(new XmlQualifiedName("Action", "http://schemas.xmlsoap.org/ws/2004/03/addressing"));
headers.Add(new XmlQualifiedName("MessageID", "http://schemas.xmlsoap.org/ws/2004/03/addressing"));
headers.Add(new XmlQualifiedName("ReplyTo", "http://schemas.xmlsoap.org/ws/2004/03/addressing"));
headers.Add(new XmlQualifiedName("To", "http://schemas.xmlsoap.org/ws/2004/03/addressing"));
c.Endpoint.Behaviors.Add(
new SignMessageHeaderBehavior(headers, "http://tempuri.org/EchoString"));
c.EchoString("123");
}
}
}
1 comments:
Hello,
This is a very interesting article. Do you also have an example how to do this the opposite way? I have to create a webservice which will receive signed requests from a client and it will have to send back signed responses with ws adressing headers using soap 11 with adressing headers. I managed to do this, but I have one problem. I am unable to add the To header in the response. Other headers like the messageId succeed, but the To header is always ignored if I try to add it to the response. If you have done something similar could you tell me how to add the to-header without the To header disappearing once I send the response?
Post a Comment