Hadler Error EndPoint in my host - WCF - Behaviour - c#

A few days ago I opened a question if I succeed with the answers. I had not focused the question well, and now with something more knowledge I ask again.
I need to capture the errors of all my endpoints to have them included in the same site. The idea is to add a behavior to these endpoints.
namespace SIPE.Search.Helpers
{
/// <summary>
/// Implements methods that can be used to extend run-time behavior for an endpoint in either a client application.
/// </summary>
public class ExternalClientBehavior : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new ExternalClientBehaviorClass();
}
public override Type BehaviorType
{
get
{
return typeof(ExternalClientBehaviorClass);
}
}
/// <summary>
/// JSON REST[GET] Converter Behavior
/// </summary>
private class ExternalClientBehaviorClass : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
ExternalClientMessageInspector clientInspector = new ExternalClientMessageInspector(endpoint);
clientRuntime.MessageInspectors.Add(clientInspector);
foreach (ClientOperation op in clientRuntime.Operations)
{
op.ParameterInspectors.Add(clientInspector);
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
//("Behavior not supported on the consumer side!");
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
}
}
Now I know that it will never enter my ApplyDispatchBehaviour if the client does not implement my behaviour, and this will NEVER happen, since they are external providers and I do not have access to the code.
Also, my first error does not even leave my code, since I'm causing a NOT FOUND error.
I have found many similar sources with my problem without solution. I have found by several sites to add the following in ApplyClientBehaviour:
IErrorHandler errorHandler = new CustomErrorHandler();
clientRuntime.CallbackDispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(errorHandler);
But this does not work.
Other sources that happened to me: https://riptutorial.com/csharp/example/5460/implementing-ierrorhandler-for-wcf-services
It is NOT a solution, since it is for Services Behavior. I need to do it in EndPoint Behavior.
Thank you

Please refer to the following example.
Server side.
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://localhost:1100");
BasicHttpBinding binding = new BasicHttpBinding();
using (ServiceHost sh = new ServiceHost(typeof(MyService), uri))
{
ServiceEndpoint se = sh.AddServiceEndpoint(typeof(IService), binding, "");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.HttpGetUrl = new Uri("http://localhost:1100/mex");
sh.Description.Behaviors.Add(smb);
}
MyEndpointBehavior bhv = new MyEndpointBehavior();
se.EndpointBehaviors.Add(bhv);
sh.Open();
Console.WriteLine("service is ready");
Console.ReadKey();
sh.Close();
}
}
}
[ServiceContract(ConfigurationName = "isv")]
public interface IService
{
[OperationContract]
string Delete(int value);
[OperationContract]
void UpdateAll();
}
[ServiceBehavior(ConfigurationName = "sv")]
public class MyService : IService
{
public string Delete(int value)
{
if (value <= 0)
{
throw new ArgumentException("Parameter should be greater than 0");
}
return "Hello";
}
public void UpdateAll()
{
throw new InvalidOperationException("Operation exception");
}
}
public class MyCustomErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
FaultException faultException = new FaultException(error.Message);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, error.Message);
}
}
public class MyEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
return;
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
MyCustomErrorHandler myCustomErrorHandler = new MyCustomErrorHandler();
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(myCustomErrorHandler);
}
public void Validate(ServiceEndpoint endpoint)
{
return;
}
}
Client.
static void Main(string[] args)
{
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
try
{
client.Delete(-3);
}
catch (FaultException fault)
{
Console.WriteLine(fault.Reason.GetMatchingTranslation().Text);
}
}
Result.
Feel free to let me know if there is anything I can help with.

Related

How to pass username and password to the SOAP header of a Web Service Call from C#?

I want to write a C# code which calls a (remote) web service in another machine. For this I have to pass username and password in the SOAP header of the call.
I would like to know an example of code to make this in C#.
the produced XML should be like :
<env:Header>
<ns1:Security>
<ns1:UsernameToken>
<ns1:Username>XXXXXXXXXXXXXXXX</ns1:Username>
<ns1:Password>YYYYYYYYYYYYYYYY</ns1:Password>
</ns1:UsernameToken>
</ns1:Security>
</env:Header>
Thanks in advance
J.
Are many ways to do that. The CustomBinding is more flexible because it allow more controll, for that i propose you with that. Pasing header to endpoint is a simple way:
// binding
var binding = new CustomBinding();
binding.Elements.Clear();
binding.Elements.Add(new TextMessageEncodingBindingElement{MessageVersion = MessageVersion.Soap12});
binding.Elements.Add(new HttpTransportBindingElement{MaxReceivedMessageSize = 20000000,});
// endpoint
var endpoint = new EndpointAddress(new Uri(listeningUri), new MySecurityHeader())
var client = new Client(binding, endpoint);
client.SomeMethod();
where MySecurityHeader is an AddressHeader, for example:
public class MySecurityHeader : AddressHeader
{
public override string Name
{
get { return "Security"; }
}
public override string Namespace
{
get { return "<provide the appropiate namespace>"; }
}
protected override void OnWriteAddressHeaderContents(System.Xml.XmlDictionaryWriter writer)
{
// here you do what you want
writer.WriteRaw(String.Format(#"
<UsernameToken xmlns=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">
<Username>user</Username>
<Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"">pass</Password>
</UsernameToken>").Trim());
}
}
this is an example using an IEndpointAdress
var customBinding = new CustomBinding();
customBinding.Elements.Add(new TextMessageEncodingBindingElement { MessageVersion = MessageVersion.Soap12, });
customBinding.Elements.Add(new HttpTransportBindingElement { MaxReceivedMessageSize = 20000000, });
var endpointAddres = new EndpointAddress(listeningUri);
var client = new Client(customBinding, endpointAddres);
// add my own IEndpointBehavior
client.ChannelFactory.Endpoint.Behaviors.Add(new CustomBehavior());
client.SomeMethod();
and this is the CustomBehavior definition
public class CustomBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
var inspector = new CustomMessageInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{}
public void Validate(ServiceEndpoint endpoint)
{}
}
public class CustomMessageInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
request.Headers.Add(new MyMessageHeader());
return null;
}
}
public class MyMessageHeader : MessageHeader
{
protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
writer.WriteRaw(String.Format(#"
<UsernameToken xmlns=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">
<Username>user</Username>
<Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"">pass</Password>
</UsernameToken>").Trim());
}
public override string Name
{
get { return "MyHeaderName"; }
}
public override string Namespace
{
get { return "MyHeaderNamespace"; }
}
}
Note you have control before send the request and after receive your reply.
I hope this resolve your issue, if you have some problems with this yust ask me.

Cookies Stripped After Being Set but Before Request

I'm having a problem where I am able to retrieve two cookies from a login call from one web service but both cookies are stripped from any requests made to a different web service.
A little background: I have a Windows service (.NET 4.5) that needs to send data to another companies web service interface. Their interface is made up of a couple different independent web services. To connect I have to call a log in web method from one of their services; I'll call it service A. The log in method returns a Long value made up of DateTime ticks as well as two cookies: a session cookie and an authorization cookie. Both cookies must be included in every request.
If I call any of the methods in service A both cookies are included in the request. Using Fiddler I have been able to verify this. However, if I attempt to call a method in service B, both cookies get stripped from the request call before it is made.
Here are the classes and code I am currently using:
CookieBehavior Class
public sealed class CookieBehavior : IContractBehavior, IEndpointBehavior, IClientMessageInspector
{
#region Private fields
public CookieContainer cookieContainer = new CookieContainer();
#endregion
#region Public constructors
public CookieBehavior()
: this(true)
{
}
public CookieBehavior(Boolean shared)
{
this.Shared = shared;
}
#endregion
#region Public properties
public Boolean Shared
{
get;
private set;
}
#endregion
#region Private methods
private void getCookies(HttpResponseMessageProperty prop, CookieContainer cookieContainer)
{
if (prop != null)
{
String header = prop.Headers[HttpResponseHeader.SetCookie];
if (header != null)
{
String[] cookies = header.Split(',');
var cc = new CookieCollection();
foreach (string cookie in cookies)
{
if (cookie.Contains(";"))
{
cc.Add(new Cookie(cookie.Substring(0, cookie.IndexOf("=")),
cookie.Substring(cookie.IndexOf("=") + 1, cookie.IndexOf(";") - (cookie.IndexOf("=") + 1))));
}
else
{
cc.Add(new Cookie(cookie.Substring(0, cookie.IndexOf("=")), cookie.Substring(cookie.IndexOf("=") + 1)));
}
}
cookieContainer.Add(new Uri(#"http://tempuri.org"), cc);
}
}
}
private void setCookies(HttpRequestMessageProperty prop, CookieContainer cookieContainer)
{
if (prop != null)
{
prop.Headers.Add(HttpRequestHeader.Cookie, cookieContainer.GetCookieHeader(new Uri(#"http://tempuri.org")));
}
}
#endregion
#region IClientMessageInspector Members
void IClientMessageInspector.AfterReceiveReply(ref Message reply, Object correlationState)
{
if (((this.Shared == true) ? CookieContext.Current.cookieContainer : this.cookieContainer).Count == 0)
{
HttpResponseMessageProperty prop = reply.Properties[HttpResponseMessageProperty.Name.ToString()] as HttpResponseMessageProperty;
this.getCookies(prop, (this.Shared == true) ? CookieContext.Current.cookieContainer : this.cookieContainer);
}
}
Object IClientMessageInspector.BeforeSendRequest(ref Message request, IClientChannel channel)
{
HttpRequestMessageProperty prop = null;
if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name.ToString()))
prop = request.Properties[HttpRequestMessageProperty.Name.ToString()] as HttpRequestMessageProperty;
else
prop = new HttpRequestMessageProperty();
this.setCookies(prop, (this.Shared == true) ? CookieContext.Current.cookieContainer : this.cookieContainer);
return (null);
}
#endregion
#region IEndpointBehavior Members
void IEndpointBehavior.AddBindingParameters(ServiceEndpoint serviceEndpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior)
{
behavior.MessageInspectors.Add(this);
}
void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher)
{
}
void IEndpointBehavior.Validate(ServiceEndpoint serviceEndpoint)
{
}
#endregion
#region IContractBehavior Members
void IContractBehavior.AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
void IContractBehavior.ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
endpoint.Behaviors.Add(this);
}
void IContractBehavior.ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
}
void IContractBehavior.Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
#endregion
}
CookieContext Class
public class CookieContext : IDisposable
{
#region Internal fields
internal CookieContainer cookieContainer = new CookieContainer();
#endregion
#region Private static fields
[ThreadStatic]
private static CookieContext current = null;
#endregion
#region Public static constructor
public CookieContext()
{
current = this;
}
#endregion
#region Public static properties
public static CookieContext Current
{
get
{
return (current);
}
}
#endregion
#region IDisposable Members
void IDisposable.Dispose()
{
this.cookieContainer.SetCookies(new Uri(#"http://tempuri.org"), String.Empty);
this.cookieContainer = null;
current = null;
}
#endregion
}
My Code
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
using (var cookieCtx = new CookieContext())
{
ServiceA.ServiceASoapClient serviceA_req = new ServiceA.ServiceASoapClient();
ServiceB.ServiceBSoapClient serviceB_req = new ServiceB.ServiceBSoapClient();
var cookieBhr = new CookieBehavior(true);
serviceA_req.Endpoint.EndpointBehaviors.Add(cookieBhr);
serviceB_req.Endpoint.EndpointBehaviors.Add(cookieBhr);
long? loginTicks = serviceA_req.InterfaceLogin();
if (loginTicks.HasValue)
{
// Get/Set data for request call
serviceB_resp = serviceB_req.SendData(fooBar);
}
}
Debugging my application has shown that both cookie headers are correctly set from the request yet they are stripped prior to the service B request.
Why would the both cookies get stripped from the second request?

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());
}

WCF exception handling using IErrorHandler

I am basically implementing the IErrorHandler interface to catch all kinds of exceptions from the WCF service and sending it to the client by implementing the ProvideFault method.
I am facing one critical issue however. All of the exceptions are sent to the client as a FaultException but this disables the client to handle specific exceptions that he may have defined in the service.
Consider: SomeException that has been defined and thrown in one of the OperationContract implementations. When the exception is thrown, its converted to a fault using the following code:
var faultException = new FaultException(error.Message);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
This does send the error as a string, but the client has to catch a general exception like:
try{...}
catch(Exception e){...}
and not:
try{...}
catch(SomeException e){...}
Not only custom exceptions like SomeException, but system exceptions like InvalidOperationException cannot be caught using the above process.
Any ideas as to how to implement this behavior?
In WCF desirable to use special exceptions described as contracts, because your client may not be .NET application which has information about standard .NET exceptions.
For do this you can define the FaultContract in the your service and then use the FaultException class.
SERVER SIDE
[ServiceContract]
public interface ISampleService
{
[OperationContract]
[FaultContractAttribute(typeof(MyFaultMessage))]
string SampleMethod(string msg);
}
[DataContract]
public class MyFaultMessage
{
public MyFaultMessage(string message)
{
Message = message;
}
[DataMember]
public string Message { get; set; }
}
class SampleService : ISampleService
{
public string SampleMethod(string msg)
{
throw new FaultException<MyFaultMessage>(new MyFaultMessage("An error occurred."));
}
}
In addition, you can specify in the configuration file that the server returns exception details in it's FaultExceptions, but this is not recommended in a production application:
<serviceBehaviors>
<behavior>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
After that, you can rewrite your method for handling exceptions:
var faultException = error as FaultException;
if (faultException == null)
{
//If includeExceptionDetailInFaults = true, the fault exception with details will created by WCF.
return;
}
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
CLIENT:
try
{
_client.SampleMethod();
}
catch (FaultException<MyFaultMessage> e)
{
//Handle
}
catch (FaultException<ExceptionDetail> exception)
{
//Getting original exception detail if includeExceptionDetailInFaults = true
ExceptionDetail exceptionDetail = exception.Detail;
}
This article might help:
http://www.olegsych.com/2008/07/simplifying-wcf-using-exceptions-as-faults/
An approach I've used with some success when I didn't want to enumerate the exceptions that might be thrown is to create a PassthroughExceptionHandlingBehavior class that implements an IErrorHandler behavior for the server-side and IClientMessageInspector for the client side. The IErrorHandler behavior serializes the exception into the fault message. The IClientMessageInspector deserializes and throws the exception.
You have to attach this behavior to both the WCF client and the WCF server. You can attach the behaviors using the config file or by applying the [PassthroughExceptionHandlingBehavior] attribute to your contract.
Here's the behavior class:
public class PassthroughExceptionHandlingBehavior : Attribute, IClientMessageInspector, IErrorHandler,
IEndpointBehavior, IServiceBehavior, IContractBehavior
{
#region IClientMessageInspector Members
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
if (reply.IsFault)
{
// Create a copy of the original reply to allow default processing of the message
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
Message copy = buffer.CreateMessage(); // Create a copy to work with
reply = buffer.CreateMessage(); // Restore the original message
var exception = ReadExceptionFromFaultDetail(copy) as Exception;
if (exception != null)
{
throw exception;
}
}
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
return null;
}
private static object ReadExceptionFromFaultDetail(Message reply)
{
const string detailElementName = "detail";
using (XmlDictionaryReader reader = reply.GetReaderAtBodyContents())
{
// Find <soap:Detail>
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element &&
detailElementName.Equals(reader.LocalName, StringComparison.InvariantCultureIgnoreCase))
{
return ReadExceptionFromDetailNode(reader);
}
}
// Couldn't find it!
return null;
}
}
private static object ReadExceptionFromDetailNode(XmlDictionaryReader reader)
{
// Move to the contents of <soap:Detail>
if (!reader.Read())
{
return null;
}
// Return the deserialized fault
try
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
return serializer.ReadObject(reader);
}
catch (SerializationException)
{
return null;
}
}
#endregion
#region IErrorHandler Members
public bool HandleError(Exception error)
{
return false;
}
public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
{
if (error is FaultException)
{
// Let WCF do normal processing
}
else
{
// Generate fault message manually including the exception as the fault detail
MessageFault messageFault = MessageFault.CreateFault(
new FaultCode("Sender"),
new FaultReason(error.Message),
error,
new NetDataContractSerializer());
fault = Message.CreateMessage(version, messageFault, null);
}
}
#endregion
#region IContractBehavior Members
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
ApplyClientBehavior(clientRuntime);
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
ApplyDispatchBehavior(dispatchRuntime.ChannelDispatcher);
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
#endregion
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
ApplyClientBehavior(clientRuntime);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
ApplyDispatchBehavior(endpointDispatcher.ChannelDispatcher);
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
#region IServiceBehavior Members
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)
{
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
ApplyDispatchBehavior(dispatcher);
}
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
#endregion
#region Behavior helpers
private static void ApplyClientBehavior(System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
foreach (IClientMessageInspector messageInspector in clientRuntime.MessageInspectors)
{
if (messageInspector is PassthroughExceptionHandlingBehavior)
{
return;
}
}
clientRuntime.MessageInspectors.Add(new PassthroughExceptionHandlingBehavior());
}
private static void ApplyDispatchBehavior(System.ServiceModel.Dispatcher.ChannelDispatcher dispatcher)
{
// Don't add an error handler if it already exists
foreach (IErrorHandler errorHandler in dispatcher.ErrorHandlers)
{
if (errorHandler is PassthroughExceptionHandlingBehavior)
{
return;
}
}
dispatcher.ErrorHandlers.Add(new PassthroughExceptionHandlingBehavior());
}
#endregion
}
#region PassthroughExceptionHandlingElement class
public class PassthroughExceptionExtension : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(PassthroughExceptionHandlingBehavior); }
}
protected override object CreateBehavior()
{
System.Diagnostics.Debugger.Launch();
return new PassthroughExceptionHandlingBehavior();
}
}
#endregion
FaultException has a Code property which you can use fore exception handling.
try
{
...
}
catch (FaultException ex)
{
switch(ex.Code)
{
case 0:
break;
...
}
}

Need example on implementing MessageHeaders.WriteHeaderContents

I have a custom header stored in a "string" variable, I need to replace the header of a outgoing SOAP request from my WCF client with the header in the "string" type variable. Based on research I see that implementing the MessageHeaders.WriteHeaderContents can work but this method accepts only XmlDictionaryWriter or XmlWriter types as input. I have a string input. How do I code in C# ..
Message headers are a SOAP concept, and SOAP requests are XML documents, so you really need some XML-ness there. But for your scenario, you actually don't need to override MessageHeaders, you can use an inspector and simply replace the header at that point, as shown in the example below (the conversion to XML will be done by the MessageHeader class).
public class StackOverflow_7141998
{
[MessageContract]
public class MyMC
{
[MessageHeader(Name = "MyHeader", Namespace = "http://my.namespace.com")]
public string HeaderValue { get; set; }
[MessageBodyMember(Name = "MyBody", Namespace = "http://my.namespace.com")]
public string BodyValue { get; set; }
}
[ServiceContract]
public interface ITest
{
[OperationContract]
void Process(MyMC mc);
}
public class Service : ITest
{
public void Process(MyMC mc)
{
Console.WriteLine("Header value: {0}", mc.HeaderValue);
}
}
public class MyInspector : IEndpointBehavior, IClientMessageInspector
{
public string NewHeaderValue { get; set; }
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(this);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
int originalIndex = request.Headers.FindHeader("MyHeader", "http://my.namespace.com");
if (originalIndex >= 0)
{
request.Headers.Insert(originalIndex, MessageHeader.CreateHeader("MyHeader", "http://my.namespace.com", this.NewHeaderValue));
request.Headers.RemoveAt(originalIndex + 1);
}
return null;
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITest), new WSHttpBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new WSHttpBinding(), new EndpointAddress(baseAddress));
MyInspector inspector = new MyInspector { NewHeaderValue = "Modified header value" };
factory.Endpoint.Behaviors.Add(inspector);
ITest proxy = factory.CreateChannel();
proxy.Process(new MyMC { HeaderValue = "Original header value", BodyValue = "The body" });
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

Categories