Hooking into wcf pipeline - c#

Is there way inspect wcf method call. I means anything equal to HttpModule in asp.net.
I want execute a method before any method call.

WCF has a very extensible architecture. It is possible to intercept and customize a lot of WCF functionality to do your own thing.
For your case you will have to write appropriate Service or Endpoint Behavior. The process between receiving a message from the transport channels and invoking your service method is called as dispatching. WCF uses a set of Behaviors to do the dispatching. You can provide your own Behavior to inspect the method calls.
This article provides an excellent overview and examples - Extending WCF with Custom Behaviors.
I would also suggest that you go through this WCF architecture overview http://msdn.microsoft.com/en-us/library/aa480210.aspx

It depends what you want to check or modify in message at what level. If you'd like to modify something at Encoding level, you can opt for Custom Message Encoder, if it's before request get deserialize or before sending reply to client, use Custom Message Inspector.
HTH.

The IOperationInvoker might have been what you were looking for:
public class MyOperationInvoker : IOperationInvoker
{
IOperationInvoker originalInvoker;
public MyOperationInvoker(IOperationInvoker originalInvoker)
{
this.originalInvoker = originalInvoker;
}
public bool IsSynchronous { get { return originalInvoker.IsSynchronous; } }
public object[] AllocateInputs() { return originalInvoker.AllocateInputs(); }
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
//Do stuff before call
var res = this.originalInvoker.Invoke(instance, inputs, out outputs);
//stuff after call
return res;
}
public IAsyncResult InvokeBegin(object instance, object[] inputs,
AsyncCallback callback, object state)
{
//Do stuff before async call
var res = this.originalInvoker.InvokeBegin(instance, inputs, callback, state);
return res;
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
var res = this.InvokeEnd(instance, out outputs, result);
//Do stuff after async call
return res;
}
}
Implemented as an attribute for all operations in the service:
public class MyBehaviorAttribute : Attribute, IServiceBehavior, IOperationBehavior
{
//IOperationBehavior
public void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new MyOperationInvoker(dispatchOperation.Invoker);
}
public void AddBindingParameters(OperationDescription operationDescription,
BindingParameterCollection bindingParameters) { /*Do nothing*/ }
public void ApplyClientBehavior(OperationDescription operationDescription,
ClientOperation clientOperation) { /*Do nothing*/ }
public void Validate(OperationDescription operationDescription) { /*Do nothing*/ }
//IServiceBehavior
public void Validate(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase) { /*Do nothing*/ }
public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters) { /*Do nothing*/ }
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceHostBase.Description.Endpoints)
{
foreach (var operation in endpoint.Contract.Operations)
{
operation.Behaviors.Add(this);
}
}
}
And the service:
[MyBehavior]
public class HelloService : IHelloService
{
...
}

Related

c# func parameter not being used

I have a legacy app that logs the input / output of services. Currently, every method has the same lines to log the request and response objects. I would like to use AOP, but without adding any extra tool (Postsharp, Castle, etc), or wrap every service class into another class (ServiceWrapper).
In order to do that, I'm trying to create a Generic class that knows that it should log the request and response objects. Here's what I'm trying:
using System;
namespace ProxyTest
{
class Program
{
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(r => fooService.DoFoo(request), "abc");
Console.Read();
}
}
class ServiceProxy
{
public static void Invoke(Func<object, object> service, object request)
{
Console.WriteLine("input:" + request);
var response = service(request);
Console.WriteLine("output:" + response);
}
}
class FooService
{
public string DoFoo(object a)
{
return a + ": returning: Do Foo";
}
}
}
Although it's working, the "abc" string is just to compile the application, but it's not being used as the request parameter. If I remove that, the code does not compile. Am I missing something?
UPDATE
Changing to the following did the trick:
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(r => fooService.DoFoo(r), request);
Console.Read();
}
You should call it like this:
class Program
{
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(fooService.DoFoo, "abc"); // lose the DoFoo parameter.
Console.Read();
}
}
You should pass the DoFoo as Func, instead of calling it. Also you should change the method signature to:
class FooService
{
public object DoFoo(object a)
{
return a + ": returning: Do Foo";
}
}
For this task you can just add logging behavior on dispatcher.
First, you create ServiceBehavior with such content:
public class ServiceLoggingBehavior : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
{
foreach (OperationDescription operation in endpoint.Contract.Operations)
{
IOperationBehavior behavior = new LoggingOperationBehavior();
operation.Behaviors.Add(behavior);
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
Then you need to create operation behavior:
internal class LoggingOperationBehavior : IOperationBehavior
{
public void Validate(OperationDescription operationDescription)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new LoggingOperationInvoker(dispatchOperation.Invoker, dispatchOperation);
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
}
And finally create invoker for all methods on server side:
internal class LoggingOperationInvoker : IOperationInvoker
{
private readonly IOperationInvoker _baseInvoker;
private readonly string _operationName;
public LoggingOperationInvoker(IOperationInvoker baseInvoker, DispatchOperation operation)
{
_baseInvoker = baseInvoker;
_operationName = operation.Name;
}
public bool IsSynchronous
{
get { return _baseInvoker.IsSynchronous; }
}
public object[] AllocateInputs()
{
return _baseInvoker.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
var sw = new Stopwatch();
try
{
LogBegin();
sw.Start();
var response = _baseInvoker.Invoke(instance, inputs, out outputs);
return response;
}
finally
{
sw.Stop();
LogEnd(sw.Elapsed);
}
}
private void LogBegin()
{
//you can log begin here.
}
private void LogEnd(TimeSpan elapsed)
{
//you can log end here.
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return _baseInvoker.InvokeBegin(instance, inputs, callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return _baseInvoker.InvokeEnd(instance, out outputs, result);
}
}
If you want to log request, you can just serialize and log inputs variable in Invoke method. For response - just serialize and log response variable.
And finaly, most enjoyable part, just attach it like attribute:
[ServiceLoggingBehavior]
public MyService : IMyServiceContract
{
...
}
Your Invoke-method clearly asks for a Func- and an object-parameter, so you have to provide both. No idea what exactly you´re expecting when you omit one of the params. I assume you want to make the Func to return the response created by a specific request-object. Furthermore it might be good idea to make your request- and response-arguments generic:
class Program
{
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(r => fooService.DoFoo(r), request);
Console.Read();
}
}
class ServiceProxy
{
public static void Invoke<TRequest, TResponse>(Func<TRequest, TResponse> service, TRequest request)
{
Console.WriteLine("input:" + request.ToString());
var response = service(request);
Console.WriteLine("output:" + response.ToString());
}
}
The Invoke-call can further be simplyfied to ServiceProxy.Invoke(fooService.DoFoo, request);
Thanks for all the responses. I was able to achieve what I was looking for using:
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(fooService.DoFoo, request);
Console.Read();
}
or
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(r => fooService.DoFoo(r), request);
Console.Read();
}

WCF Add Header Issue with AddressHeader.CreateAddressHeader() method

I'm trying to add a custom header to every outgoing request message via WCF.
I created the header object like that:
EndpointAddressBuilder eab = new EndpointAddressBuilder(combinedService.Endpoint.Address);
AddressHeader addressHeader = AddressHeader.CreateAddressHeader("HeaderData", String.Empty, "String data");
eab.Headers.Add(addressHeader);
combinedService.Endpoint.Address = eab.ToEndpointAddress();
I use this exact code in two positions in my code, one works well but the other does not.
The problem is in the following line of code:
AddressHeader addressHeader = AddressHeader.CreateAddressHeader("HeaderData", String.Empty, "String data");
When it works (pass the header data successfully) the created object looks like that:
But when it doesn't work, the created object looks like that:
The exact same method does this but two position code calls yield different results.
Is there any method I should execute on the addressHeader object to force it serialize the object? Maybe something like: Flush()?
I KNOW I can use several well known patterns of adding a custom headers like: "custom behavior", "Client Message Inspector" etc... But I have a requirement to add it on a specific point right before we send the message.
I've, finally, got a solution.
I just followed the steps in this and this excellent, detailed and simple articles.
Although this code might seems to be long and complecated, this is the most right manner for handling header data in WCF. So it'll finally worth it.
You just have to configure custom behavior for handling WCF header.
Here is how it goes:
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.
Important: Note that you'll get an error in the app config after adding these lines of configuration code:
Don't worry about this, your application will run correctly. It causes because the GAC (Global Assembly Cache) folder doesn't contain this behavior (since it is 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.

How to handle exceptions thrown in IParameterInspector?

I've been looking all over the place and I can't seem to find the answer. I have an extension endpoint behaviour which uses IParameterInspector. How can I handle an exception when it's thrown in BeforeCall method?
I've tried adding try-catch to IEndPointBehavior and BehaviorExtensionElement neither of which handles it. Here is some code:
BehaviorExtensionElement:
public class ExtensionService : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
//try-catch doesn't work here
return new ExtensionBehavior();
}
public override Type BehaviorType
{
get { return typeof(ExtensionBehavior); }
}
}
IEndpointBehavior:
public class ExtensionBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
//throw new NotImplementedException();
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
foreach (ClientOperation clientOperation in clientRuntime.ClientOperations)
{
//try-catch here doesn't work
clientOperation.ClientParameterInspectors.Add(new ParamInspector());
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (DispatchOperation dispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
{
//try-catch here doesn't work
dispatchOperation.ParameterInspectors.Add(new ParamInspector());
}
}
public void Validate(ServiceEndpoint endpoint)
{
//throw new NotImplementedException();
}
}
IParameterInspector
public class ParamInspector : IParameterInspector
{
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
}
public object BeforeCall(string operationName, object[] inputs)
{
///an exception is thrown here
return null;
}
}
I did finally manage to solve it. I had to implement IErrorHandler like so:
public class CustomErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
//the procedure for handling the errors.
//False is returned because every time we have an exception we want to abort the session.
return false;
}
public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
{
}
}
and then add this IErrorHandler to ApplyDispatchBehavior
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (DispatchOperation dispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
{
dispatchOperation.ParameterInspectors.Add(new ParamInspector(this.Class));
}
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new CustomErrorHandler());
}

Add behaviorattribute to a WorkflowServiceHost

Hi all i have a problem while adding a custom behavior to a WorkflowServiceHost.
Here is my WorflowServiceHostFactory:
public class ScoringWorkflowServiceHostFactory : WorkflowServiceHostFactory, IServiceHost<IKernel>
{
private static IKernel _InjectionInstance;
public IKernel InjectionInstance
{
get { return _InjectionInstance ?? (_InjectionInstance = new StandardKernel(new ScoringWorkflowServicesNinjectModule(Scope))); }
}
public object Scope
{
get { return Guid.NewGuid(); }
}
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
String fullFilePath = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, constructorString);
WorkflowService wf = CSharpExpressionCompiler.Compile(fullFilePath);
System.ServiceModel.Activities.WorkflowServiceHost host = base.CreateWorkflowServiceHost(wf, baseAddresses);
NinjectBehaviorAttributeWF behavior = new NinjectBehaviorAttributeWF(wf);
host.Description.Behaviors.Add(behavior);
host.AddNinjectResolverExtension(InjectionInstance, Scope);
TypeAdapterFactory.SetCurrent(new SvcMapperAdapterFactory());
LoggerFactory.SetCurrent(new EntLibLoggerFactory());
return host;
}
}
Here is my behavior:
public class NinjectBehaviorAttributeWF : Attribute, IServiceBehavior
{
private System.ServiceModel.Activities.WorkflowService host;
public NinjectBehaviorAttributeWF(System.ServiceModel.Activities.WorkflowService host)
{
this.host = host;
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in dispatcher.Endpoints)
{
DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime;
dispatchRuntime.InstanceContextProvider = new PerCallInstanceContextProvider(dispatchRuntime);
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
In this way, i have an error while loading my service(xamlx): The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.
I don't know neither it's possible, nor how can create the default constructor for a workflowservice, (because the real implementation is the xamlx and not a simple class)
So, I've tried with a custom Provider:
dispatchRuntime.InstanceProvider = new CustomInstanceProvider(host.Body);
where CustomInstanceProvider is:
public class CustomInstanceProvider : IInstanceProvider
{
string message;
private System.Activities.Activity activity;
public CustomInstanceProvider(string msg)
{
Console.WriteLine("The non-default constructor has been called.");
this.message = msg;
}
public CustomInstanceProvider(System.Activities.Activity activity)
{
this.activity = activity;
}
public object GetInstance(InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
{
Console.WriteLine("GetInstance is called:");
return this.activity;
}
public object GetInstance(InstanceContext instanceContext)
{
Console.WriteLine("GetInstance is called:");
return this.activity;
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
Console.WriteLine("ReleaseInstance is called");
}
}
But i have this error:
System.InvalidCastException: Unable to cast object of type 'System.ServiceModel.Activities.WorkflowService' to type 'IHttpGetMetadata'.
How can I resolve my problem? Thanks a lot

how to restrict access to WCF restful function to specific IP range?

I've built a Restful WCF service with few simple functions. a new requirememt has been raised.
one of the functions should be accessible only to a specific ip range.
what is the best way to implement this? I thought that an easy way is to simply configure the IIS with a rule that will block ip range according the request pattern - cant find such option..
Thanks!
ofer
Have you tried implementing IParameterInspector? Your code could look something like this:
public class IPFilterAttribute : Attribute, IOperationBehavior, IParameterInspector
{
private string _rangeFrom;
private string _rangeTo;
public IPFilterAttribute(string rangeFrom, string rangeTo)
{
_rangeFrom = rangeFrom;
_rangeTo = rangeTo;
}
public void ApplyDispatchBehavior(
OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
dispatchOperation.ParameterInspectors.Add(this);
}
public void AfterCall(string operationName, object[] outputs,
object returnValue, object correlationState)
{
}
public object BeforeCall(string operationName, object[] inputs)
{
RemoteEndpointMessageProperty clientEndpoint =
OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
if (!IsClientInInRange(clientEndpoint.Address))
{
throw new SecurityException(string.Format("Calling method '{0}' is not allowed from address '{1}'.", operationName, clientEndpoint.Address));
}
return null;
}
private bool IsClientInRange(string clientAddress)
{
// do the magic to check if client address is in the givn range
}
public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void Validate(OperationDescription operationDescription)
{
}
}
Then all you have to do is decorate the web method with this attribute:
[OperationContract]
[WebInvoke(...)]
[IPFilter("64.18.0.0", "64.18.15.255")]
string GetData(string value);
couple options:
- you can use a firewall to do this job for you
IIS has capabilities that can block ip, but you will have to host your service in IIS.
you can use WCF to get the client address and then accept/deny the call.
Refer:
http://www.danrigsby.com/blog/index.php/2008/05/21/get-the-clients-address-in-wcf/

Categories