WCF Same IParameterInspector for all operations on a service - wcf

I have implemented a custom IParameterInspector and I want to have it execute for every single operation on my service.
My understanding is that IParameterInspector implementations can only be used with IOperationBehavior implementations, and that intern IOperationBehavior implementation can only be used to decorate individual operations using an attribute.
Does anyone know if there is a way I can register my IParameterInspector at a service level so that it can execute for all operations in the service?

Thanks to this and subsequenbtly this, I found what I was looking for.
IParameterInspector does not need to be at the IOperationBehavior level. They can be at the IServiceBehavior level. In the service level ApplyDispatchBehavior method you need to loop through all its operations and assign the inspector behaviour.
My class in full...
[AttributeUsage(AttributeTargets.Class)]
public class ServiceLevelParameterInspectorAttribute : Attribute, IParameterInspector, IServiceBehavior
{
public object BeforeCall(string operationName, object[] inputs)
{
// Inspect the parameters.
return null;
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
}
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 (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
if (channelDispatcher == null)
{
continue;
}
foreach(var endPoint in channelDispatcher.Endpoints)
{
if (endPoint == null)
{
continue;
}
foreach(var opertaion in endPoint.DispatchRuntime.Operations)
{
opertaion.ParameterInspectors.Add(this);
}
}
}
}
}

Related

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

How to add control the cache of a restful service operation by operation?

I've written a rest service using WCF. The service contains several operations. Some are GET based ([WebGet]), others are POST based ([WebInvoke]).
The service is working as expected. However, the GET based operations are put in the client cache, which is not desirable for all operations.
After a bit a search, I've found How to prevent the browser from caching WCF JSON responses. This is working, but I found it's not very reusable.
My platform does not allows me to update the web.config. Actually, my service is part of a SharePoint project. and updating the web.config file is hard to implement properly. This forbid me to use the [WebCache] attribute.
So I implemented a custom MessageInspector which fix the proper headers:
public class CacheAttribute : Attribute, IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase host)
{
foreach (ChannelDispatcher cDispatcher in host.ChannelDispatchers)
{
foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
{
eDispatcher.DispatchRuntime.MessageInspectors.Add(new CacheInspector(m_CacheEnabled, CacheDuration));
}
}
}
/*...
Other code omitted for brievty
*/
}
public class CacheInspector : IDispatchMessageInspector
{
/*...
Code omitted for brievety
*/
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
var cache = HttpContext.Current.Response.Cache;
if (m_CacheEnabled)
{
cache.SetCacheability(HttpCacheability.Public);
cache.SetExpires(DateTime.UtcNow + CacheDuration.Value);
}
else
{
cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
cache.SetNoStore();
}
}
}
This code is working as expected, but it applies to all operations in the service.
How can I code an attribute based class that apply the same logic, but at the operation scope ?
I've tried to find something useful in the IOperationBehavior interface, but I did not find the appropriate implementation.
Full code (.net 4.5):
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class CacheAttribute : Attribute, IServiceBehavior
{
private readonly bool m_CacheEnabled;
public bool CacheEnabled { get { return m_CacheEnabled; } }
public TimeSpan? CacheDuration { get; set; }
public CacheAttribute(bool cacheEnabled)
{
this.m_CacheEnabled = cacheEnabled;
}
public CacheAttribute(TimeSpan cacheDuration) : this(true)
{
this.CacheDuration = cacheDuration;
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase host)
{
foreach (ChannelDispatcher cDispatcher in host.ChannelDispatchers)
{
foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
{
eDispatcher.DispatchRuntime.MessageInspectors.Add(new CacheInspector(m_CacheEnabled, CacheDuration));
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
public class CacheInspector : IDispatchMessageInspector
{
private readonly bool m_CacheEnabled;
private readonly TimeSpan? CacheDuration;
public CacheInspector(bool m_CacheEnabled, TimeSpan? CacheDuration)
{
this.m_CacheEnabled = m_CacheEnabled;
this.CacheDuration = CacheDuration;
}
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
var cache = HttpContext.Current.Response.Cache;
if (m_CacheEnabled)
{
cache.SetCacheability(HttpCacheability.Public);
cache.SetExpires(DateTime.UtcNow + CacheDuration.Value);
}
else
{
cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
cache.SetNoStore();
}
}
}
I think this is what your are looking for.
[AttributeUsage(AttributeTargets.Method)]
public class CacheAttribute : Attribute, IOperationBehavior, IParameterInspector
{
public TimeSpan CacheLifetime { get; private set; }
public CacheAttribute(double lifetime)
{
this.CacheLifetime = TimeSpan.FromSeconds(lifetime);
}
#region IOperationBehavior Members
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) {}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) {}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.ParameterInspectors.Add(this);
}
public void Validate(OperationDescription operationDescription) {}
#endregion
#region IParameterInspector Members
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
if (this.CacheLifetime == TimeSpan.Zero) {
WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", "no-cache");
} else {
WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", string.Format("max-age={0}",this.CacheLifetime.TotalSeconds));
}
}
public object BeforeCall(string operationName, object[] inputs)
{
return null;
}
#endregion
}
usage
[ServiceContract]
public interface ICacheTestService
{
[OperationContract]
[WebGet(UriTemplate = "CurrentTime")]
[Cache(0)]
string GetCurrentTime();
[OperationContract]
[WebGet(UriTemplate = "CurrentTimeCached")]
[Cache(30)]
string GetCurrentTimeCached();
[OperationContract]
[WebGet(UriTemplate = "CurrentTimeNoCC")]
string GetCurrentTimeNoCC();
}

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.

How to add global error handling in wcf rest service

In my web applications I make use of the Application_Error function in global.asax to log all exceptions like so:
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
while (ex.GetBaseException() != null)
{
ex = ex.GetBaseException();
}
log.writeError(ex.ToString());
}
I've tried similiar in a WCF REST service with no luck. How would I add global error handling? I saw this article, but I'm new to implementing the IServiceBehavior. Where would I add the above code?
I use:
1) AppDomain.CurrentDomain.UnhandledException event
2) TaskScheduler.UnobservedTaskException event
3) IErrorHandler:
public class ErrorHandler : IErrorHandler
{
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
var faultException = new FaultException<string>("Server error: " + error.Format());
var messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, null);
}
public bool HandleError(Exception error)
{
return false;
//return true; //if handled
}
}
[AttributeUsage(AttributeTargets.Class)]
public class ErrorHandlerBehavior : Attribute, IEndpointBehavior, IServiceBehavior
{
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ErrorHandler());
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
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 (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
channelDispatcher.ErrorHandlers.Add(new ErrorHandler());
}
}
}
This can be applied to entire service impl. class:
[ErrorHandlerBehavior]
public class SubscriberInfoTaskService : {}
or to endpoint:
var endpoint = Host.Description.Endpoints.FirstOrDefault();
//foreach (ChannelDispatcher channelDispatcher in Host.ChannelDispatchers) //ChannelDispatcherBase
//{
// channelDispatcher.ErrorHandlers.Add(new ErrorHandler());
//}
endpoint.Behaviors.Add(new ErrorHandlerBehavior());
here about using config: http://www.steverb.com/post/2008/11/24/Useful-WCF-Behaviors-IErrorHandler.aspx

How can I extend WCF in the Callback direction?

I can attach an IParameterInspector using behaviors to each operation in the ClientRuntime and also to each operation in the DispatchRuntime on the service side. But it seems this only works from client to service.
I also want to be able to attach an IParameterInspector in the callbacks from service to client on both sides of the wire as above but I can't find any extensibility points to do this.
Any ideas?
This is a little obscure and does not appear to be all that well documented but you can customise both ends using standard WCF behavior capabilities.
On the client, this attribute would make it happen.
public class InspectorBehaviorAttribute : Attribute, IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
foreach (var item in clientRuntime.CallbackDispatchRuntime.Operations)
{
item.ParameterInspectors.Add(ParameterInspector.Instance);
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
Simply apply this attribute on the class that implements your callback interface.
On the server, it gets a little trickier. You need to hook up via the ApplyDispatchBehavior. In this case I have done it through a service behavior but the principal applies to OperationBehaviors and EndpointBehaviors as well.
public class InspectorBehaviorAttribute : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (var item in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>())
{
foreach (var ep in item.Endpoints)
{
foreach (var op in ep.DispatchRuntime.CallbackClientRuntime.Operations)
{
op.ParameterInspectors.Add(ParameterInspector.Instance);
}
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
Again, simply apply this attribute to your service implementation to have your parameter inspector utilised for all callback operations.
While these examples demonstrate hooking up IParameterInspector implementations, the same approach for all other WCF extension points can be used to customise callback channels at both the client and server.