IErrorHandler doesn't seem to be handling my errors in WCF .. any ideas?
Asked Answered
G

2

24

Have been reading around on IErrorHandler and want to go the config route. so, I have read the following in an attempt to implement it.

MSDN

Keyvan Nayyeri blog about the type defintion

Rory Primrose Blog

This is basically just the msdn example wrapped in a class that inherits IErrorHandler and IServiceBehaviour ... then this is wrapped in the Extension element that inherits from BehaviourExtensionElement to allegedly allow me to add the element into the web.config. What have i missed?

I have got it to compile and from the various errors i have fixed it seems like WCF is actually loading the error handler. My problem is that the exception that i am throwing to handle in the error handler doesn;t get the exception passed to it.

My service implementation simply calls a method on another class that throws ArgumentOutOfRangeException - however this exception never gets handled by the handler.

My web.config

<system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="basic">
          <security mode="None" />                      
        </binding>
      </basicHttpBinding>
    </bindings>
    <extensions>
      <behaviorExtensions>
        <add name="customHttpBehavior"
             type="ErrorHandlerTest.ErrorHandlerElement, ErrorHandlerTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <serviceBehaviors>
        <behavior name="exceptionHandlerBehaviour">          
          <serviceMetadata httpGetEnabled="true"/>
          <!-- 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"/>
          <customHttpBehavior />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="exceptionHandlerBehaviour" name="ErrorHandlerTest.Service1">
        <endpoint binding="basicHttpBinding" bindingConfiguration="basic" contract="ErrorHandlerTest.IService1" />
      </service>
    </services>

Service Contract

[ServiceContract]
public interface IService1
{
    [OperationContract]
    [FaultContract(typeof(GeneralInternalFault))]
    string GetData(int value);
}

The ErrorHandler class

public class ErrorHandler : IErrorHandler , IServiceBehavior 
{
    public bool HandleError(Exception error)
    {
        Console.WriteLine("caught exception {0}:",error.Message );
        return true;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
       if (fault!=null )
       {
           if (error is ArgumentOutOfRangeException )
           {
               var fe = new FaultException<GeneralInternalFault>(new GeneralInternalFault("general internal fault."));
               MessageFault mf = fe.CreateMessageFault();

               fault = Message.CreateMessage(version, mf, fe.Action);

           }
           else
           {
               var fe = new FaultException<GeneralInternalFault>(new GeneralInternalFault(" the other general internal fault."));
               MessageFault mf = fe.CreateMessageFault();

               fault = Message.CreateMessage(version, mf, fe.Action);
           }
       }
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {

    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        IErrorHandler errorHandler = new ErrorHandler();
        foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
            if (channelDispatcher != null)
            {
                channelDispatcher.ErrorHandlers.Add(errorHandler);
            }
        }
    }


    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {


    }
}

And the Behaviour Extension Element

    public class ErrorHandlerElement : BehaviorExtensionElement 
    {
        protected override object CreateBehavior()
        {
            return new ErrorHandler();
        }

        public override Type BehaviorType
        {
            get { return typeof(ErrorHandler); }
        }
    }
Gangue answered 14/6, 2010 at 11:8 Comment(0)
P
44

Here's a full working example:

[ServiceContract]
public interface IService1
{
    [OperationContract]
    [FaultContract(typeof(MyFault))]
    string GetData(int value);
}

[DataContract]
public class MyFault
{

}

public class Service1 : IService1
{
    public string GetData(int value)
    {
        throw new Exception("error");
    }
}

public class MyErrorHandler : IErrorHandler
{
    public bool HandleError(Exception error)
    {
        return true;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message msg)
    {
        var vfc = new MyFault();
        var fe = new FaultException<MyFault>(vfc);
        var fault = fe.CreateMessageFault();
        msg = Message.CreateMessage(version, fault, "http://ns");
    }
}

public class ErrorHandlerExtension : BehaviorExtensionElement, IServiceBehavior
{
    public override Type BehaviorType
    {
        get { return GetType(); }
    }

    protected override object CreateBehavior()
    {
        return this;
    }

    private IErrorHandler GetInstance()
    {
        return new MyErrorHandler();
    }

    void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        IErrorHandler errorHandlerInstance = GetInstance();
        foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
        {
            dispatcher.ErrorHandlers.Add(errorHandlerInstance);
        }
    }

    void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
        {
            if (endpoint.Contract.Name.Equals("IMetadataExchange") &&
                endpoint.Contract.Namespace.Equals("http://schemas.microsoft.com/2006/04/mex"))
                continue;

            foreach (OperationDescription description in endpoint.Contract.Operations)
            {
                if (description.Faults.Count == 0)
                {
                    throw new InvalidOperationException("FaultContractAttribute not found on this method");
                }
            }
        }
    }
}

and web.config:

<system.serviceModel>
  <services>
    <service name="ToDD.Service1">
      <endpoint address=""
                binding="basicHttpBinding"
                contract="ToDD.IService1" />
    </service>
  </services>

  <behaviors>
    <serviceBehaviors>
      <behavior>
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="false"/>
        <errorHandler />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <extensions>
    <behaviorExtensions>
      <add name="errorHandler"
            type="ToDD.ErrorHandlerExtension, ToDD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </behaviorExtensions>
  </extensions>

</system.serviceModel>
Pardue answered 5/7, 2010 at 8:50 Comment(4)
thank you very much, sorry took so long for me to answer my setup at work is knackered. This worked fine on my setup at home though.Gangue
Worked for me, even though visual studio complains "the element behavior has invalid child element errorhandler" - I just ignore and on runtime it works.Lemley
How do I make it return a json object of a custom type (eg MyFault)?Nottinghamshire
Here is how to make it return json: #1149537Nottinghamshire
M
1

You can see if the web.config is working and loading by adding a print or a breakpoint to the ApplyDispatchBehavior, and see if that gets printed/hit when the service first opens. So is it being loaded?

I'd add a print/breakpoint at ProvideFault, as well.

Millar answered 5/7, 2010 at 8:42 Comment(1)
The breakpoints don't get hit. the only breakpoint that i can get hit is in the webservice itself.Gangue

© 2022 - 2024 — McMap. All rights reserved.