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/
Related
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.
Is there something Like ActionFilterAttribute (From ASP.NET MVC) in WCF Services (Or anything like that). basically what I want to do is to log what is coming and goint to and from my services, and I don't want to write the logging code in every single ServiceContracts. yes the question is very general but you understand the idea what I want to do.
Yes there is it called as MessageInspectors/ParameterInspectors , here you can read about them
http://msdn.microsoft.com/en-us/library/aa717047%28v=vs.110%29.aspx
This is exactly what you are looking for , WCF custom behavior log logging
http://www.codeproject.com/Articles/243352/LoggingBehavior-How-to-Connect-Log-prints-with-the
Only confusing thing is you can have message inspector on WCF service and WCF proxy as well , in your case you need only for service side
I had to read a lot to find this out, I'm not an expert in WCF but given this information is a little scarce I'm sharing what have worked for me.
My Solution consists of using an OperationBehavior and a DispatcherMessageInspector
OperationBehavior
Allows you to change the binding information, validate de operation
description, and apply dispatcher behaviors.
DispatcherMessageInspector
Allows you to inspect and change the messages that are sent for your
service.
Dispatcher
Gets the messages from the communication channels and sends to the
right operation, and get the result back to the caller.
Service Operation
are your service methods
CODE SOLUTION
MESSAGE INSPECTOR
public class MyMessageInspector : IDispatchMessageInspector
{
List<string> targetOperations = new List<string>();
public MyMessageInspector(OperationDescription operation)
{
this.AddOperation(operation);
}
public void AddOperation(OperationDescription operation)
{
this.targetOperations.Add(operation.Messages[0].Action);
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
if (TargetOperationMatchesRequest(request))
{
request = ChangeMessage(request);
return true;
}else
{
return false;
}
}
public bool TargetOperationMatchesRequest(Message request)
{
string requestAction = request.Headers.To.AbsolutePath;
requestAction = requestAction.Substring(requestAction.LastIndexOf("/"));
string targetOperation = "";
foreach (string targetOperationPath in targetOperations)
{
targetOperation = targetOperationPath.Substring(targetOperationPath.LastIndexOf("/"));
if (targetOperation.Equals(requestAction))
{
return true;
}
}
return false;
}
public Message ChangeMessage(Message oldMessage)
{
Message newMessage = request.CreateBufferedCopy(Int32.MaxValue).CreateMessage();
//Change your message
return newMessage;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
}
OPERATION
public class MyOperation : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
MyMessageInspector inspector = dispatchOperation.Parent.MessageInspectors
.Where(x => x is MyMessageInspector)
.FirstOrDefault() as MyMessageInspector;
if (inspector != null)
{
inspector.AddOperation(operationDescription);
}
else
{
inspector = new MessageInspectors(operationDescription);
dispatchOperation.Parent.MessageInspectors.Add(inspector);
}
}
public void Validate(OperationDescription operationDescription)
{
}
}
CONTRACT
[ServiceContract]
public interface IService
{
[OperationContract]
[MyOperation]
OutputData MyMethod(InputData inputData);
}
SERVICE
public class Service : IService
{
[WebInvoke(Method = "POST", UriTemplate = "/json/MyMethod", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public OutputData MyMethod(InputData inputData)
{
//Implementation
return new OutputData();
}
}
I implemented an IClientMessageInspector to "intercept" outgoing web service call in my application. Is it possible to find out which operation is being called from inside the BeforeSendRequest and AfterReceiveReply?
There is a similar question here, How do i get the invoked operation name within a WCF Message Inspector, which is for the server side (the side receiving the request). I tried to do something similar, e.g.
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var v = OperationContext.Current.OutgoingMessageProperties["HttpOperationName"];
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
var v = OperationContext.Current.OutgoingMessageProperties["HttpOperationName"];
}
but during outgoing request it seems that OperationContext.Current is null, so I cannot use this. Any idea how to get it? Any idea how to do it cleanly (as opposed, to say, parse the SOAP xml)?
From the comments you asked how doing this could be done with IParameterInspector. The operation name is part of the Before/AfterCall methods.
Just to add to my comments on which inspector to use. From Carlos Figueira's blogs:
The message inspectors, described in the previous post of this series,
allows you complete control over the message going through the WCF
stack. They’re very powerful, but you have to know how to deal with
the Message object, which is not the most desirable way of
programming. If the service model in WCF hides all the messaging
framework by allowing us to define our services in terms of
strongly-typed operations (i.e., using nice primitive and user defined
types), there should be a way of intercepting requests / responses
after all the processing to extract those parameters from incoming
messages (or before they’re packaged in outgoing messages) is done.
The IParameterInspector is exactly that – before and after each call,
the inspector gets a chance to inspect the operation inputs, outputs
and return value, in the same types as defined by the operation
contract, no conversion needed (the only thing needed is a cast, since
the parameters are passed as objects).
This is a complete command line program that demonstrates:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace WCFClientInspector
{
public class OperationLogger : IParameterInspector
{
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
Console.WriteLine("Completed operation:" + operationName);
}
public object BeforeCall(string operationName, object[] inputs)
{
Console.WriteLine("Calling operation:" + operationName);
return null;
}
}
public class OperationLoggerEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
foreach (ClientOperation operation in clientRuntime.ClientOperations)
{
operation.ClientParameterInspectors.Add(new OperationLogger());
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
[ServiceContract]
public interface ISimple
{
[OperationContract]
void DoSomthing(string s);
}
public class SimpleService : ISimple
{
public void DoSomthing(string s)
{
Console.WriteLine("Called:" + s);
}
}
public static class AttributesAndContext
{
static void Main(string[] args)
{
ServiceHost simpleHost = new ServiceHost(typeof(SimpleService), new Uri("http://localhost/Simple"));
simpleHost.Open();
ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(simpleHost.Description.Endpoints[0]);
factory.Endpoint.EndpointBehaviors.Add(new OperationLoggerEndpointBehavior());
ISimple proxy = factory.CreateChannel();
proxy.DoSomthing("hi");
Console.WriteLine("Press ENTER to close the host.");
Console.ReadLine();
((ICommunicationObject)proxy).Shutdown();
simpleHost.Shutdown();
}
}
public static class Extensions
{
static public void Shutdown(this ICommunicationObject obj)
{
try
{
obj.Close();
}
catch (Exception ex)
{
Console.WriteLine("Shutdown exception: {0}", ex.Message);
obj.Abort();
}
}
}
}
It should give the output:
Calling operation:DoSomthing
Called:hi
Completed operation:DoSomthing
Press ENTER to close the host.
What about reply.Headers.Action and request.Headers.Action. Of course the rest is same tricky as in question linked. So the full code will be:
var action = reply.Headers.Action.Substring(reply.Headers.Action.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1);
or
var action = request.Headers.Action.Substring(request.Headers.Action.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1);
My wcf service is being hosted in iis7. Is there a way I could put break point at ApplyDispatchBehavior method and make sure that its being called at run time by hitting that breakpoint?? My overall problem is that method AfterReceiveRequest is not being hit in the CultureMessageInspector class. Details of my code have been posted under questions in the following links. Please help me.. thanks
wcf AfterReceiveRequest not getting called
passing culture value inside wcf service hosted by iis
public class CultureBehaviour : IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
CultureMessageInspector inspector = new CultureMessageInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
CultureMessageInspector inspector = new CultureMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
}
public class CultureMessageInspector : IClientMessageInspector, IDispatchMessageInspector
{
private const string HeaderKey = "culture";
#region IDispatchMessageInspector Members
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
int headerIndex = request.Headers.FindHeader(HeaderKey, string.Empty);
if (headerIndex != -1)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(request.Headers.GetHeader<String>(headerIndex));
}
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
#endregion
#region IClientMessageInspector Members
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader(HeaderKey, string.Empty, Thread.CurrentThread.CurrentCulture.Name));
return null;
}
#endregion
}
Presuming that the PDB files are present in your bin directory you can simply attach a debugger to the IIS7 process and debug as normal.
Make sure that the web.config has debug="true" set.
See this answer for more details
Attach Debugger to IIS instance
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
{
...
}