Is it possible to pass in WCF client proxy metadata info in request headers? - wcf

We want some ability to version our client proxies that we generate using a simple YYYY-MM model, or a simple integer, and have server side visibility on this through headers.

This is sample code on the client:
public class ClientMessageLogger : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
MessageHeader header1 = MessageHeader.CreateHeader("Testreply", "http://Test", "Test");
request.Headers.Add(header1);
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageHeader header = MessageHeader.CreateHeader("UserAgent", "http://User", "User1");
reply.Headers.Add(header);
return null;
}
}
[AttributeUsage(AttributeTargets.Interface)]
public class CustomBehavior : Attribute, IContractBehavior
{
public Type TargetContract => typeof(ServiceReference1.ICalculator);
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new ClientMessageLogger());
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
return;
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
Add [CustomBehavior] above the interface.
Here is the reference: IClientMessageInspector

Related

UsernameTokenHeader for WCF service

I am new WCF service. Here I am trying to add wsse:Security UsernameToken Header in wcf request message but I don't know how to do that and the rest of the things. It would be much appreciated if someone could help on this.
Thanks in advance.
You can add a custom header by implementing the IDispatchMessageInspector interface. IDispatchMessageInspector is the interface implemented by the server. The client needs to implement the IClientMessageInspector interface.
Here is my Demo:
public class CustomMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
MessageHeader header = MessageHeader.CreateHeader("Username", "http://Username", "Username");
OperationContext.Current.OutgoingMessageHeaders.Add(header);
}
}
This is the header added in the SOAP message.If you need to add a header to the http request, refer to the following code:
public class CustomMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
WebOperationContext Context = WebOperationContext.Current;
Context.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
}
}
CustomMessageInspector implements the IDispatchMessageInspector interface, and adds a custom header to the message after getting the message, and also adds a custom header before sending the message.
[AttributeUsage(AttributeTargets.Interface)]
public class CustomBehavior : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
return;
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.MessageInspectors.Add(new CustomMessageInspector());
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
We add this interceptor to the behavior of the service.
Finally we apply Custombehavior to our service.

WCF One method to execute before every service method's call

I am looking for a way to execute specific method, at the server side, on every request method's call.
This is for security validations but not only.
This is NOT duplicated with this question since we mean to completely different things and. I addition, all the relevant answers there have unavailable links so it's impossible to get to the right answer.
(Sorry I haven't attached any code here, there is no code to specify in this issue).
The best solution is to create WCF custom behavior.
Here is how you do this by several simple steps:
Client Side:
public class FillHeaderDataBehaviourExtension : BehaviorExtensionElement, IEndpointBehavior
{
#region BehaviorExtensionElement Implementation
public override Type BehaviorType
{
get
{
return typeof(FillHeaderDataBehaviourExtension);
}
}
protected override object CreateBehavior()
{
return this;
}
#endregion
#region IServiceBehaviour Implementation
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new MessageInspector());
}
#endregion
}
public class MessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageHeader header = MessageHeader.CreateHeader("HeaderData", String.Empty, HeaderDataVM.GetInstance().GetBaseInstance());
request.Headers.Add(header); // There is no need for checking if exist before adding. Every request has it's own headers.
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
}
Server Side:
public class ExtractHeadersBehaviourExtension : BehaviorExtensionElement, IServiceBehavior
{
#region BehaviorExtensionElement Implementation
public override Type BehaviorType
{
get
{
return typeof(ExtractHeadersBehaviourExtension);
}
}
protected override object CreateBehavior()
{
return this;
}
#endregion
#region IServiceBehavior Implementation
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++)
{
ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher;
if (channelDispatcher != null)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
MessageInspector inspector = new MessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
#endregion
}
public class MessageInspector : IDispatchMessageInspector
{
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HeaderData headerData = request.Headers.GetHeader<HeaderData>("HeaderData", String.Empty);
if(headerData != null)
{
OperationContext.Current.IncomingMessageProperties.Add("HeaderData", headerData);
}
return null;
}
}
And finally, don't forget to configure it in the app.config files (client & server side) as follows:
<behaviors>
<endpointBehaviors>
<behavior name="NewBehavior">
<fillHeaderDataBehaviourExtension/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
You can also add these lines via the WCF config editor. To do so, look at this answer.
EDIT: You might get an error in the app config after adding these lines of configuration code:
Don't worry about this, your application will run fine. It causes because the GAC (Global Assembly Cache) folder doesn't contain this behavior (since it is a custom behavior). You can fix it by adding this behavior manually to your GAC folder on your computer.
However, this error might prevent you from updating service reference. If you try to, you'll get this error message:
So just comment out this line (<extractHeadersBehaviourExtension/>) (in client & server side) when you update your service reference.
Sources: How to add behavior on a specific endpoint? &
Adding Custom Message Headers to a WCF Service

Catching generic FaultException<T> thrown from IErrorHandler?

For a service, there is a group of faults which can be thrown by all operations, so in order to centralize that, I have made a behavior, FaultAdderBehavior, which adds fault contracts to all operations on a service. It seems to work fine as the contracts are added to the WSDL and the client can catch the fault with a line like:
...
catch(FaultException<MyFault> e){ ... }
...
I have also made an IErrorHandler which converts non-fault exceptions to a certain kind of fault. See below.
The problem is, that the fault which is constructed in the error handler cannot be caught on the client. That is, it cannot be caught as a generic FaultException<MyFault>, but only as FaultException.
If I explicitly adds a FaultContract(typeof(MyFault)) to the operation, the client can suddenly catch the generic fault exception just fine.
So that could indicate that something is wrong with my FaultAdderBehavior after all. Or is there something wrong with my error handler?
I have noticed, that the fault.Action given as argument to CreateMessage() is null. This raises my concern.
Below is an example illustrating the problem. It is the method ShouldThrowFault() that causes headaches, while ThrowsDirectly() works exactly as desired.
To summarize, my question is: why can't the client catch the generic FaultException<MyFault> when it comes from the error handler?
[ServiceContract]
public interface IUncatchableFaultService
{
[OperationContract]
// [FaultContract(typeof(MyFault))]
void ShouldThrowFault(string arg1);
[OperationContract]
void ThrowsDirectly();
}
[FaultAdderBehavior(typeof(MyFault), typeof(MyFault2))]
[MyErrorHandlerBehavior]
internal class UncatchableFaultService : IUncatchableFaultService
{
public void ShouldThrowFault(string arg1)
{
throw new Exception();
}
public void ThrowsDirectly()
{
throw new FaultException<MyFault>(new MyFault());
}
}
[DataContract]
public class MyFault
{
}
[DataContract]
public class MyFault2
{
}
public class MyErrorHandlerBehaviorAttribute : Attribute, IServiceBehavior
{
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase dispatcherBase in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher channelDispatcher = dispatcherBase as ChannelDispatcher;
if (channelDispatcher == null) continue;
channelDispatcher.ErrorHandlers.Add(new MyErrorHandler());
}
}
private class MyErrorHandler : IErrorHandler
{
public void ProvideFault(Exception error, MessageVersion version, ref Message message)
{
if (error is FaultException) return;
var fault = new FaultException<MyFault>(new MyFault(), "I am a fault.");
MessageFault messageFault = fault.CreateMessageFault();
message = Message.CreateMessage(version, messageFault, fault.Action);
}
public bool HandleError(Exception error)
{
return false;
}
}
}
public class FaultAdderBehaviorAttribute : Attribute, IContractBehavior
{
private Type[] faults;
public FaultAdderBehaviorAttribute(params Type[] faults)
{
this.faults = faults;
}
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
foreach (OperationDescription op in contractDescription.Operations)
foreach (Type fault in this.faults)
op.Faults.Add(this.ExposeFault(fault));
}
private FaultDescription ExposeFault(Type fault)
{
string action = fault.Name;
DescriptionAttribute attr = (DescriptionAttribute)Attribute.GetCustomAttribute(fault, typeof(DescriptionAttribute));
if (attr != null) action = attr.Description;
FaultDescription description = new FaultDescription(action);
description.DetailType = fault;
description.Name = fault.Name;
return description;
}
}
There is a problem with FaultAdderBehaviorAttribute and you are right to be worried that fault.Action is null.
For FaultException functionality to work properly you must have non-null actions for each fault.
When you declare the FaultContract on the operation itself you are implicitly using WCF's ability to automatically generate the action string (further details). However, when you use your FaultAdderBehaviorAttribute, any default actions for declared operations have already been generated, and you are failing to provide a valid action.

IClientMessageInspector detect one-way operation

I need to do some operation logging in IClientMessageInspector and knowning when an operation has started and ended is vital. I can't however get AfterReceiveReply for one-way operations which is clear why. Is there a way to know that an operation is one-way in BeforeSendRequest overload so I could just ignore it?
There's no information on the inspector itself (or the message passed to BeforeSendRequest), but you can pass this information to the inspector, and use the message action to see whether the operation is one way or not.
public class StackOverflow_10354828
{
[ServiceContract]
public interface ITest
{
[OperationContract]
string Echo(string text);
[OperationContract(IsOneWay = true)]
void Process(string input);
}
public class Service : ITest
{
public string Echo(string text)
{
return text;
}
public void Process(string input) { }
}
class MyInspector : IClientMessageInspector
{
public HashSet<string> oneWayActions;
public MyInspector(ServiceEndpoint endpoint)
{
this.oneWayActions = new HashSet<string>();
foreach (var operation in endpoint.Contract.Operations)
{
if (operation.IsOneWay)
{
oneWayActions.Add(operation.Messages[0].Action);
}
}
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
Console.WriteLine("In AfterReceiveReply");
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
Console.WriteLine("In BeginSendRequest");
if (this.oneWayActions.Contains(request.Headers.Action))
{
Console.WriteLine("This is a one-way operation");
}
return null;
}
}
class MyBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new MyInspector(endpoint));
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
factory.Endpoint.Behaviors.Add(new MyBehavior());
ITest proxy = factory.CreateChannel();
proxy.Echo("Hello");
Console.WriteLine();
proxy.Process("world");
Console.WriteLine();
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
Replying to myself, a the moment I'm doing this:
bool isOneWay = request.Headers.ReplyTo == null;

Enabling SOAP Message Inspection for a Single Operation

How can I force an IMessageInspector to fire only when a specific operation is called, rather than firing whenever a call to the service is made? Currently, I am applying a custom IEndpointBehavior to the endpoint which calls this IMessageInspector, but this is not exactly what I would like to do...
The message inspectors are bound to the dispatch runtime object, which is a single one for each endpoint, not operation. If a parameter inspector works, then you can use it (it's bound to the operation), but if you need a message inspector, then it cannot be bound to a single operation. But you can, inside your inspector, check whether the operation is what you expect (based on the Action header, which is unique per operation), as shown in the code below.
public class StackOverflow_7502134
{
[ServiceContract]
public interface ITest
{
[OperationContract]
string Echo(string text);
[OperationContract]
int Add(int x, int y);
}
public class Service : ITest
{
public string Echo(string text)
{
return text;
}
public int Add(int x, int y)
{
return x + y;
}
}
public class MyInspector : IEndpointBehavior, IDispatchMessageInspector
{
string[] operationNames;
public MyInspector(params string[] operationNames)
{
this.operationNames = operationNames ?? new string[0];
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
}
public void Validate(ServiceEndpoint endpoint)
{
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
// by default, action == <serviceContractNamespace>/<serviceContractName>/<operationName>
string operationName = request.Headers.Action.Substring(request.Headers.Action.LastIndexOf('/') + 1);
if (this.operationNames.Contains(operationName))
{
Console.WriteLine("Inspecting request to operation {0}", operationName);
Console.WriteLine(request);
Console.WriteLine();
return operationName;
}
else
{
return null;
}
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
string operationName = correlationState as string;
if (operationName != null)
{
Console.WriteLine("Inspecting reply from operation {0}", operationName);
Console.WriteLine(reply);
Console.WriteLine();
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
MyInspector inspector = new MyInspector("Add"); // inspecting Add, not Echo
host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "").Behaviors.Add(inspector);
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine("Calling Echo");
Console.WriteLine(proxy.Echo("Hello world"));
Console.WriteLine();
Console.WriteLine("Calling Add");
Console.WriteLine(proxy.Add(4, 5));
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}