I have made a custom error handler on the service side:
public class GlobalErrorHandler : Attribute, IErrorHandler, IServiceBehavior
{
public void AddBindingParameters(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandler = new GlobalErrorHandler();
foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
if (channelDispatcher != null)
{
channelDispatcher.ErrorHandlers.Add(errorHandler);
}
}
}
public bool HandleError(Exception error)
{
Trace.TraceError(error.ToString());
if (error is FaultException)
return false; // Let WCF do normal processing
else
return true; // Fault message is already generated
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (error is FaultException)
{
// Let WCF do normal processing
}
else
{
// Generate fault message manually
MessageFault messageFault = MessageFault.CreateFault(
new FaultCode("Sender"), new FaultReason(error.Message),
error, new NetDataContractSerializer());
fault = Message.CreateMessage(version, messageFault, null);
}
}
}
public class ErrorHandlerElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new GlobalErrorHandler();
}
public override Type BehaviorType
{
get { return typeof (GlobalErrorHandler); }
}
}
I have defined a custom message inspector on the client side :
public class MessageInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
if (reply.IsFault)
{
//do some processing
}
}
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
{
return null;
}
}
I have a custom behaviour which wire ups the message inspector :
public class NewtonsoftJsonBehavior : WebHttpBehavior
{
public override void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new MessageInspector());
}
}
and this behaviour is applied programatically through a factory :
public class JsonWebServiceHostFactory : WebServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
var host = base.CreateServiceHost(serviceType, baseAddresses);
//return host;
//ServiceEndpoint ep = host.AddServiceEndpoint(serviceType, new WebHttpBinding(), "");
//host.Description.Endpoints[0].Behaviors.Add(new WebHttpBehavior { HelpEnabled = true });
//return host;
WebHttpBinding webBinding = new WebHttpBinding();
host.AddServiceEndpoint(serviceType, webBinding, "").Behaviors.Add(new NewtonsoftJsonBehavior());
return host;
}
}
However when I debug and I generate a faultexception in the service, the globalerrorhandler gets called , but the debugger never steps into the message inspector.
Any idea why?
To create a Message Inspector on WCF service side, use an implementation of IDispatchMessageInspector instead of: IClientMessageInspector.
An example:
Service:
EndpointAddress endpoint = new EndpointAddress("http://localhost:9001/Message");
WebServiceHost svcWebHost = new WebServiceHost(typeof(Service.Message), endpoint.Uri);
CustomServiceBehavior serviceBehavior = new CustomServiceBehavior();
svcWebHost.Description.Behaviors.Add(serviceBehavior);
Binding webHttpBinding = new WebHttpBinding();
ServiceEndpoint serviceEndpoint = svcWebHost.AddServiceEndpoint(typeof(Service.IMessage), webHttpBinding, endpoint.Uri);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
svcWebHost.Description.Behaviors.Add(smb);
ServiceDebugBehavior sdb = svcWebHost.Description.Behaviors.Find<ServiceDebugBehavior>();
sdb.IncludeExceptionDetailInFaults = true;
svcWebHost.Open();
Service Contract
[ServiceContract]
public interface IMessage
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Model.TestResponse Test();
}
Service Implementation
public class Message : IMessage
{
public Model.TestResponse Test()
{
return new Model.TestResponse() { success = true, message = "OK!" };
}
}
CustomServiceBehavior implements IServiceBehavior:
public class CustomServiceBehavior : IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
CustomEndpointBehavior endpointBehavior = new CustomEndpointBehavior();
foreach (var endpoint in serviceDescription.Endpoints)
endpoint.EndpointBehaviors.Add(endpointBehavior);
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
}
CustomEndpointBehavior implements IEndpointBehavior
public class CustomEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
var inspector = new CustomDispatchMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
CustomDispatchMessageInspector implements IDispatchMessageInspector
public class CustomDispatchMessageInspector : IDispatchMessageInspector
{
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 httpResponse = ((HttpResponseMessageProperty)reply.Properties["httpResponse"]);
httpResponse.Headers.Add("user-agent", "My Browser");
}
}
This example is a WCF Self Hosted without configuration file (Configuring WCF Services in Code) that return a Json and send a custom header in HTTP Response (user-agent:My Browser).
To test this code:
Create a Windows Console Application
Insert the code (each
block in one Class)
Run the applcation
Using browser open url:
http://localhost:9001/Message/Test
The response is a Json:
{"message":"OK!","success":true}
You can inspect the Response and
see the custom header: "user-agent:My Browser"
You only put your message inspector on the client side using ApplyClientBehavior. There is another method for the service side:
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
if (endpointDispatcher != null)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MessageInspector());
}
}
Related
I have a standalone C# WCF service running as a Windows service. I have the requirement to add custom headers like X-Frame-Options to all responses. I have tried to add an instance of the following class to ServiceEndpoint.Behaviors
internal class ServerInterceptor : IDispatchMessageInspector, IEndpointBehavior
{
object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
return null;
}
void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
{
reply.Properties.Add("X-Frame-Options", "deny");
}
void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
}
void IEndpointBehavior.Validate(ServiceEndpoint endpoint) { }
void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
}
This doesn't add any HTTP header to the response although the class gets called as the debugger can step into the BeforeSendReply function. Furthermore if I replace reply.Properties with reply.Headers then the header is added, but not to the HTTP headers but to the SOAP headers.
How can I add a HTTP header like X-Frame-Options to the response?
I made an example, which is used to add extra CORS HTTP header, wish it is instrumental for you.
Message Inspector.
public class CustomHeaderMessageInspector : IDispatchMessageInspector
{
Dictionary<string, string> requiredHeaders;
public CustomHeaderMessageInspector(Dictionary<string, string> headers)
{
requiredHeaders = headers ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
string displayText = $"Server has received the following message:\n{request}\n";
Console.WriteLine(displayText);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
if (!reply.Properties.ContainsKey("httpResponse"))
reply.Properties.Add("httpResponse", new HttpResponseMessageProperty());
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
string displayText = $"Server has replied the following message:\n{reply}\n";
Console.WriteLine(displayText);
}
}
Custom Contract Attribute.
public class MyBehaviorAttribute : Attribute, IContractBehavior, IContractBehaviorAttribute
{
public Type TargetContract => typeof(MyBehaviorAttribute);
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)
{
var requiredHeaders = new Dictionary<string, string>();
requiredHeaders.Add("Access-Control-Allow-Origin", "*");
requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
requiredHeaders.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
dispatchRuntime.MessageInspectors.Add(new CustomHeaderMessageInspector(requiredHeaders));
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
}
Apply the contract behavior.
[ServiceContract(Namespace = "mydomain")]
[MyBehavior]
public interface IService
{
[OperationContract]
[WebGet]
string SayHello();
}
Result.
Feel free to let me know if there is anything I can help with.
I'm trying to inject a some business services in a WCF service. I read this really interessting post: How do I pass values to the constructor on my wcf service?
And I have done the following:
Custom ServiceHost
public class UnityServiceHost : ServiceHost
{
public UnityServiceHost(IUnityContainer container, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
foreach (ContractDescription contractDescription in ImplementedContracts.Values)
{
contractDescription.Behaviors.Add(new UnityInstanceProvider(serviceType, container));
}
}
}
Custom Instance Provider
public class UnityInstanceProvider : IInstanceProvider, IContractBehavior
{
private readonly Type m_serviceType;
private readonly IUnityContainer m_container;
public UnityInstanceProvider(Type serviceType, IUnityContainer container)
{
m_serviceType = serviceType;
m_container = container;
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
return GetInstance(instanceContext);
}
public object GetInstance(InstanceContext instanceContext)
{
return m_container.Resolve(m_serviceType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
(instance as IDisposable)?.Dispose();
}
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)
{
dispatchRuntime.InstanceProvider = this;
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint) { }
}
The service behavior that I've is looking like this:
[ServiceBehavior]
public class MyService : IMyService
{
public ServerInformationService(ISomeDependency coreManager)
{
//...
}
}
But I don't get into the GetInstance, and when I run the code, I get this:
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.
What have I done wrong? It look like my instance provider isn't even used
I finally found the solution. I was applying my InstanceProvider to Contact and not to services.
Here is my final (Working) solution:
public class UnityServiceHost : ServiceHost
{
public UnityServiceHost(IUnityContainer container, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
Description.Behaviors.Add(new UnityInstanceProvider(serviceType, container));
}
}
And the behavior+instance provider:
public class UnityInstanceProvider : IInstanceProvider, IServiceBehavior
{
private readonly Type m_serviceType;
private readonly IUnityContainer m_container;
public UnityInstanceProvider(Type serviceType, IUnityContainer container)
{
m_serviceType = serviceType;
m_container = container;
}
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 channelDispatcherBase in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = (ChannelDispatcher)channelDispatcherBase;
foreach (EndpointDispatcher ed in cd.Endpoints)
{
if (!ed.IsSystemEndpoint)
{
ed.DispatchRuntime.InstanceProvider = this;
}
}
}
}
public object GetInstance(InstanceContext instanceContext)
{
return m_container.Resolve(m_serviceType);
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
return GetInstance(instanceContext);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
(instance as IDisposable)?.Dispose();
}
}
It works because I'm self-hosting the service(new UnityServiceHost(...) ...., if it was on IIS, I could not create the behavior that way
We are working on WCF Routing Service which redirects different soap actions to different endpoints.
We want to this service rewrite query string included in router url: #router url#?param=param to endpoint: #endpoint url#?param=param.
Our webservices accepts query strings when call directly, this strings are visible in router (context) but on the end this strings are removed from url.
Do you know how add this strings to the end of endpoint url in every request?
We solved the problem.
You must create new binding:
public class QueryHttpBinding : BasicHttpBinding
{
public override BindingElementCollection CreateBindingElements()
{
var result = base.CreateBindingElements();
var http = result.Find<HttpTransportBindingElement>();
if (http != null)
{
http.ManualAddressing = true;
}
var https = result.Find<HttpsTransportBindingElement>();
if (https != null)
{
https.ManualAddressing = true;
}
return result;
}
}
And Client message inspector:
public class CustomInspectorBehavior : IClientMessageInspector
{
object IClientMessageInspector.BeforeSendRequest(ref Message request, IClientChannel channel)
{
UriBuilder builder = new UriBuilder(channel.RemoteAddress.ToString());
builder.Path += "?" + ((HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]).QueryString;
request.Headers.To = builder.Uri;
return null;
}
void IClientMessageInspector.AfterReceiveReply(ref Message reply, object correlationState)
{
}
}
Next you must create new endpoint Behavior:
public class Behavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
var inspector = new CustomInspectorBehavior();
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
I am now developing a WCF service using Visual Studio 2013. My service contains a custom header field must be passed. Field name is "lisenseKey" I developed it. WCF service is working fine. Now I am about to call my service to test. I added a service reference to client project. Now I am calling like this.
MyService.Myservice proxy = new MyService.Myservice();
proxy.LisenseKey = "xxxxxx";
Label1.Text = proxy.GetMessage(TextBox1.Text);
When I assign lisense key value like this, it is showing error with red underline that "Cannot implicitly convert type string to MyService.MyService.string". How can I type cast in this condition? Or is there any way to pass header ? And I do not know how to use "MessageHeader".
Here is my server side code. I am trying to consume "GetMessage" method.
//using System.ServiceModel;
//using System.ServiceModel.Dispatcher;
//using System.ServiceModel.Channels;
//using System.ServiceModel.Description;
//using System.Collections.ObjectModel;
[ServiceContract]
public interface IMyservice
{
[OperationContract]
void DoWork();
[OperationContract]
string GetMessage(string name);
}
public class MyServiceMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
if(request.Headers.FindHeader("LisenseKey","")==-1)
{
throw new FaultException("Lisense Key Was Not Provided");
}
var lisenseKey = request.Headers.GetHeader<string>("LisenseKey", "");
if (string.IsNullOrEmpty(lisenseKey))
{
throw new FaultException("Lisnse key is not valid");
}
if(lisenseKey!="12345x")
{
throw new FaultException("Lisense key is not valid");
}
return instanceContext;
}
public void BeforeSendReply(ref Message reply,object correlationState)
{
}
}
public class MyServiceMessageInspectorBehaviour:Attribute, IServiceBehavior
{
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)
{
foreach(var endpointDispatcher in channelDispatcher.Endpoints)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyServiceMessageInspector());
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
Is it possible to handle errors like TimeoutException, EndpointNotFoundException, OperationTimeoutException, etc on client side using BehaviorExtension?
I don't want to try-catch-log-restart-connection every time when i'm using WCF proxy.
This code does not working:
Behavyior:
public class EndpointBehavior:IEndpointBehavior
{
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)
{
var handler = new WcfErrorHandler();
clientRuntime.CallbackDispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(handler);
}
}
Handler:
public class WcfErrorHandler:IErrorHandler
{
public bool HandleError(Exception error)
{
Log.Instance.Trace(#"WCF Service Error:", error);
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
var newEx = new FaultException(
string.Format(#"Exception caught at WcfErrorHandler{0} Method: {1}{2}Message:{3}",
Environment.NewLine, error.TargetSite.Name, Environment.NewLine, error.Message));
var msgFault = newEx.CreateMessageFault();
fault = Message.CreateMessage(version, msgFault, newEx.Action);
}
}
Extension for app.config
public class ExceptionLogBehaviorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get
{
return typeof(EndpointBehavior);
}
}
protected override object CreateBehavior()
{
return new EndpointBehavior();
}
}
Your code is not working because IErrorHandler is a server-side-only feature.
In your code, it looks like you are adding the error handler to client-side requests, but actually that's not the case. That's because clientRuntime.CallbackDispatchRuntime is a server-like entity that lives in the client and is used to receive messages from the real server in duplex operations.
Regarding your main question about client-side exception handling, the best I can recommend is IClientMessageInspector. Here's some code to help you get started:
public sealed class LoggingEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new LoggingInspector());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
}
public sealed class LoggingInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
Console.WriteLine("BeforeSendRequest");
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
Console.WriteLine("AfterReceiveReply");
}
}
However, note that IClientMessageInspector has a drawback. Its AfterReceiveReply method is called on successful and faulty replies from the server, but is not called when there isn't any reply at all, e.g. on TimeoutException, EndpointNotFoundException, etc.