Reputation: 979
By defining an attribute that implements IContactBehavior and IWsdlExportExtension and set that attribute on your service contract, you can easily add Soap Headers to your wsdl (see http://wcfextras.codeplex.com/ for more information)
But now I need to set a Soap Header contract in the wsdl on all Operationcontracts and this time I cannot set an attribute.
Following code (called from IWsdlExportExtension.ExportEndPoint) doesn't work, but does work when called from the SoapHeaderAttributes (that executes an IWsdlExportExtension.ExportContract)
foreach (OperationDescription operationDescription in context.ContractConversionContext.Contract.Operations)
{
AddSoapHeader(operationDescription, "SomeHeaderObject", typeof(SomeHeaderObject), SoapHeaderDirection.InOut);
}
internal static void AddSoapHeader(OperationDescription operationDescription, string name, Type type, SoapHeaderDirection direction)
{
MessageHeaderDescription header = GetMessageHeader(name, type);
bool input = ((direction & SoapHeaderDirection.In) == SoapHeaderDirection.In);
bool output = ((direction & SoapHeaderDirection.Out) == SoapHeaderDirection.Out);
foreach (MessageDescription msgDescription in operationDescription.Messages)
{
if ((msgDescription.Direction == MessageDirection.Input && input) ||
(msgDescription.Direction == MessageDirection.Output && output))
msgDescription.Headers.Add(header);
}
}
internal static MessageHeaderDescription GetMessageHeader(string name, Type type)
{
string headerNamespace = SoapHeaderHelper.GetNamespace(type);
MessageHeaderDescription messageHeaderDescription = new MessageHeaderDescription(name, headerNamespace);
messageHeaderDescription.Type = type;
return messageHeaderDescription;
}
Anyone has an idea how to apply this code on all operations (without using attributes) and by doing this, adding the contract of the header to the wsdl ?
Upvotes: 4
Views: 18814
Reputation: 1185
Easiest way is to use WCFExtrasPlus "https://wcfextrasplus.codeplex.com/wikipage?title=SOAP%20Headers&referringTitle=Documentation", just add dll as reference to your project and edit your service in such way:
[DataContract(Name="MyHeader", Namespace="web")]
public class MyHeader
{
[DataMember(Order=1)]
public string UserName {get; set;}
[DataMember(Order=2)]
public string Password { get; set; }
}
[SoapHeaders]
[ServiceContract]
public interface IMyService
{
[SoapHeader("MyHeader", typeof(MyHeader), Direction = SoapHeaderDirection.In)]
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped)]
bool MyMethod(string input);
}
Then Soap request looks like:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="web" xmlns:tem="http://tempuri.org/">
<soapenv:Header>
<web:MyHeader>
<web:UserName>?</web:UserName>
<web:Password>?</web:Password>
</web:MyHeader>
</soapenv:Header>
<soapenv:Body>
<tem:MyMethod>
<tem:input>?</tem:input>
</tem:MyMethod>
</soapenv:Body>
</soapenv:Envelope>
If you need Soap and JSON at same service here is example: https://stackoverflow.com/a/23910916/3667714
Upvotes: 0
Reputation: 979
The IEndpointBehavior has the following interface:
ApplyDispatchBehavior(ServiceEndpoint endPoint, EndPointDispatcher endpointDispatcher);
You can add Soap Headers to the wsdl for operations by iterating over the endpoint.Contract.Operations in the ApplyDispatchBehavior.
Here you have the complete solution that worked for me:
void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (OperationDescription operationDescription in endpoint.Contract.Operations)
{
foreach (MessageDescription msgDescription in operationDescription.Messages)
{
AddSoapHeader(operationDescription, "SomeHeaderObject", typeof(SomeHeaderObject), SoapHeaderDirection.InOut);
}
}
}
internal static void AddSoapHeader(OperationDescription operationDescription, string name, Type type, SoapHeaderDirection direction)
{
MessageHeaderDescription header = GetMessageHeader(name, type);
bool input = ((direction & SoapHeaderDirection.In) == SoapHeaderDirection.In);
bool output = ((direction & SoapHeaderDirection.Out) == SoapHeaderDirection.Out);
foreach (MessageDescription msgDescription in operationDescription.Messages)
{
if ((msgDescription.Direction == MessageDirection.Input && input) ||
(msgDescription.Direction == MessageDirection.Output && output))
msgDescription.Headers.Add(header);
}
}
internal static MessageHeaderDescription GetMessageHeader(string name, Type type)
{
string headerNamespace = SoapHeaderHelper.GetNamespace(type);
MessageHeaderDescription messageHeaderDescription = new MessageHeaderDescription(name, headerNamespace);
messageHeaderDescription.Type = type;
return messageHeaderDescription;
}
The SoapHeaderHelper can be found in the WcfExtras.
Upvotes: 5
Reputation: 755207
You might want to have a look at the WCFExtras project on CodePlex - it has some support for custom SOAP headers and stuff like that. Not 100% sure if it's capable of filling your need, but check it out!
Marc
UPDATE: have you looked into creating a WCF extension, e.g. something like a message inspector, on both the client and the server side?
The client side IClientMessageInspector defines two methods BeforeSendRequest
and AfterReceiveReply
while the server side IDispatchMessageInspector has the opposite methods, i.e. AfterReceiveRequest
and BeforeSendReply
.
With this, you could add headers to every message going across the wire (or selectively only to a few).
Here's a snippet from a IClientMessageInspector implementor we use to automagically transmit the locale information (language and culture info) across from the clients to the server - should give you an idea how to get started:
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
International intlHeader = new International();
intlHeader.Locale = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
MessageHeader header = MessageHeader.CreateHeader(WSI18N.ElementNames.International, WSI18N.NamespaceURI, intlHeader);
request.Headers.Add(header);
return null;
}
Upvotes: 4