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);
Related
AFAIK WCF has a very powerful configurable logging infrastructure, but in my case it's too complex. I want to implement something simple like access.log with pattern similar to this
%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"
Problem is that WCF is logging in XML in very complex format, and it's kinda verbose. Maybe there is some way to simplify this XML? It's ok that it's an XML instead of textfile, but it has multiple fields and data that takes space, makes log harder to read and so on.
The only way I found for now is implement my own IOperationInvoker for it, but maybe I can reuse builtin logging system? Please, advice.
I implemented it with custom behaviour. Here is implementation:
class LoggingBehaviour : IEndpointBehavior
{
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new LoggingMessageInspector());
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
}
and custom logging inspector:
public class LoggingMessageInspector : IDispatchMessageInspector
{
private static readonly Logger CurrentClassLogger = LogManager.GetCurrentClassLogger();
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
return request.Headers.To;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
var requestUri = (Uri)correlationState;
var currentContext = WebOperationContext.Current;
if (currentContext == null)
{
CurrentClassLogger.Log(LogLevel.Error, "Cannot log reply to [{0}]: WebOperationContext is null", requestUri);
return;
}
try
{
var httpRequest = currentContext.IncomingRequest;
string host = httpRequest.Headers[HttpRequestHeader.Host];
string method = httpRequest.Method;
string userAgent = httpRequest.UserAgent;
var statusCode = currentContext.OutgoingResponse.StatusCode;
CurrentClassLogger.Log(LogLevel.Info, "[Host {0}] [{1} {2} {3} {4}] [{5}]", host, method, requestUri, (int) statusCode, statusCode, userAgent);
}
catch (Exception ex)
{
CurrentClassLogger.Error("Cannot log reply to [{0}] : {1}", requestUri, ex);
}
}
}
Then use it!
foreach (var endpoint in ServiceHost.Description.Endpoints)
{
endpoint.Behaviors.Add(new LoggingBehaviour());
}
My intention is to detect unhandled errors inside a WCF service, log them and shutdown the application.
For this purpose I use WCF's IErrorHandler. In the method HandleError(Exception error) I am notified that an exception occured. Everything works ok. At the end of the question you will find complete listing. Here is the output:
00000: Starting service ...
00041: Client call ThrowUnexpected
00056: Service is throwing [InvalidOperationException]
00063: Client chatched [FaultException]
10070: ErrorHandler got [TimeoutException]
10070: ErrorHandler got [InvalidOperationException]
There are two things I am unhappy about:
Instead of expected InvalidOperationException I first get TimeoutException and then the one I have thrown. If I would log and shutdown after the first one I will have wrong information in my log.
The callback does not arrive immediately, only after about 10 seconds. These seems to be exactly those timeout seconds which are probably default for net.tcp. It is too late for me because I wont to terminate the process immediately after something unexpected happened.
Question 1: Is it a bug or is it normal that I get my exception only on second place? Can I assume that for any WCF configuration I will get this pair of exceptions? Is there any way to get only the exception which was thrown inside the method?
Question 2: Is there any way to be called immediately and not after timeout?
Listing:
internal class Program
{
private static void Main(string[] args)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine("{0:00000}: Starting service ...", stopwatch.ElapsedMilliseconds);
var instance = new SomeService(stopwatch);
var uri = new UriBuilder(Uri.UriSchemeNetTcp, IPAddress.Loopback.ToString(), 8085, "SomeService").Uri;
using (var host = new ServiceHost(instance))
{
host.AddServiceEndpoint(typeof (ISomeService), new NetTcpBinding(), uri);
host.Description.Behaviors.Add(new ErrorHandlerBehavior(new ErrorHandler(stopwatch)));
host.Open();
// DO NOT DISPOSE Channel is broken
var proxy = new SomeServiceProxy(uri);
{
try
{
Console.WriteLine("{0:00000}: Client call ThrowUnexpected", stopwatch.ElapsedMilliseconds);
proxy.ThrowUnexpected();
}
catch (FaultException ex)
{
Console.WriteLine("{0:00000}: Client chatched [{1}]", stopwatch.ElapsedMilliseconds,
ex.GetType().Name);
}
}
}
}
}
}
[ServiceContract]
public interface ISomeService
{
[OperationContract]
void ThrowUnexpected();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class SomeService : ISomeService
{
private readonly Stopwatch _stopwatch;
public SomeService(Stopwatch stopwatch)
{
_stopwatch = stopwatch;
}
public void ThrowUnexpected()
{
var exception = new InvalidOperationException();
Console.WriteLine("{0:00000}: Service is throwing [{1}]", _stopwatch.ElapsedMilliseconds,
exception.GetType().Name);
throw exception;
}
}
public class ErrorHandler : IErrorHandler
{
private readonly Stopwatch _stopwatch;
public ErrorHandler(Stopwatch stopwatch)
{
_stopwatch = stopwatch;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
}
public bool HandleError(Exception error)
{
Console.WriteLine("{0:00000}: ErrorHandler got [{1}]", _stopwatch.ElapsedMilliseconds, error.GetType().Name);
return false;
}
}
public class SomeServiceProxy : ClientBase<ISomeService>, ISomeService
{
public SomeServiceProxy(Uri uri)
: base(new NetTcpBinding(), new EndpointAddress(uri))
{
}
public void ThrowUnexpected()
{
Channel.ThrowUnexpected();
}
}
public class ErrorHandlerBehavior : IServiceBehavior
{
private readonly IErrorHandler m_Handler;
public ErrorHandlerBehavior(IErrorHandler errorHandler)
{
m_Handler = errorHandler;
}
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 (var channelDispatcherBase in serviceHostBase.ChannelDispatchers)
{
var dispatcher = (ChannelDispatcher) channelDispatcherBase;
dispatcher.ErrorHandlers.Add(m_Handler);
}
}
}
I think you have a small misunderstanding how the IErrorHandler works. I refer to the MSDN. First comes the ProvideFault method
All ProvideFault implementations are called first, prior to sending a response message. When all ProvideFault implementations have been called and return, and if fault is non-null, it is sent back to the client according to the operation contract. If fault is null after all implementations have been called, the response message is controlled by the ServiceBehaviorAttribute.IncludeExceptionDetailInFaults property value.
then comes the HandleError method
Because the HandleError method can be called from many different places there are no guarantees made about which thread the method is called on. Do not depend on HandleError method being called on the operation thread.
The TimeoutException you are seeing comes from the closing of the ServiceHost (end of the using-Block). You can control this by setting the CloseTimeout on the ServiceHost.
host.CloseTimeout = TimeSpan.FromSeconds(2);
Why does the Timeout happend at all?
This is because, the connection from the proxy to the service is still there and not closed, even when the proxy is in the faulted state. To resolve this you need to call Abort in the catch-block of the FaultedException.
catch (FaultException ex)
{
proxy.Abort();
Console.WriteLine("{0:00000}: Client chatched [{1}]", stopwatch.ElapsedMilliseconds,
ex.GetType().Name);
}
This will result in the following output
00000: Starting service ...
00005: Client call ThrowUnexpected
00010: Service is throwing [InvalidOperationException]
00014: Client chatched [FaultException]
00026: ErrorHandler got [CommunicationException]
00029: ErrorHandler got [InvalidOperationException]
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 have a simple WCF webservice with 2 methods : one save/updates an obect in the cache and the other one deletes it. How can I save the object when I close the webservice server.
Using CacheItemRemovedCallback doesn't work because the object is removed everytime i update it.
Using Global.asax.cs.Application_End() doesn't work also because the cache is cleared by the time it get here.
Using Dispose() method method doesn't work because it get called every time a call has finished.
[ServiceContract]
public class WebService
{
[OperationContract]
public void Test(string message)
{
List<string> Logs;
Logs = HttpRuntime.Cache.Get("LogMessages") as List<string>;
if (Logs == null)
{
Logs = new List<string>();
Logs.Add(message);
}
else Logs.Add(message);
HttpRuntime.Cache.Insert("LogMessages", Logs, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, null);
}
[OperationContract]
public void WriteToFile()
{
List<string> Logs;
Logs = HttpRuntime.Cache.Get("LogMessages") as List<string>;
if (Logs == null)
{
string filename = DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss_fff");
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
//any method of writing the object to disk
HttpRuntime.Cache.Remove("LogMessages");
});
}
}
}
Generally, if you need to do something when a WCF service starts or stops it should be done by extending the ServiceHostFactory.
Check ServiceHost OnClose or OnClosing event to do what your need.
public class DerivedHost : ServiceHost
{
public DerivedHost( Type t, params Uri baseAddresses ) :
base( t, baseAddresses ) {}
protected override void OnClose(System.TimeSpan timeout)
{
...
base.OnClose(timeout);
}
protected override void OnClosing()
{
...
base.OnClosing();
}
}
You can also implement your own instance provider and use the ReleaseInstance method.
public class MyInstanceProviderBehavior : IInstanceProvider
{
...
#region IInstanceProvider Members
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
...
}
#endregion
}
For more information about WCF extensibility, take a look at Carlos Figueira blog.
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/