WCF exception handling of different exception types

Topics: Exception Handling Application Block
Nov 30, 2012 at 5:52 AM

We want to use exception handling application block to convert verious types of WCF service side exceptions into a fault contract, they should have different messages in resource configured in EHAB's config file.

We are using exception shielding, not try-catch in the code.
The problem is all the exceptions converted have the same message as Exception, unexpectedly. While debuging in the logging handler, we found that all exceptions shielded are of type FaultException, not their original type.


Is it wrong or by design? If by design, how can we distinct them?

 

this is the config

<exceptionHandling>
    <exceptionPolicies>
      <add name="ServiceExceptionPolicy">
        <exceptionTypes>
          <add name="All Exceptions" type="System.Exception, mscorlib, Version=4.0.0.0"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Logging Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0"
                logCategory="ExceptionLogging" eventId="100" severity="Error"
                title="Enterprise Library Exception Handling" formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
                priority="0" />
              <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null"
                exceptionMessageResourceType="SomeNameSpace1.Properties.ExceptionResources,SomeProject1"
                exceptionMessageResourceName="errAllException" exceptionMessage=""
                faultContractType="SomeNameSpace2.SomeFaultContract, SomeProject2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Fault Contract Exception Handler">
                <mappings>
                  <add source="{Message}" name="FaultMessage" />
                  <add source="{Guid}" name="FaultID" />
                </mappings>
              </add>
            </exceptionHandlers>
          </add>
          <add name="ArgumentValidationException" type="Microsoft.Practices.EnterpriseLibrary.Validation.PolicyInjection.ArgumentValidationException, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Logging Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0"
                logCategory="ExceptionLogging" eventId="100" severity="Warning"
                title="Enterprise Library Exception Handling" formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
                priority="0" />
              <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null"
                exceptionMessageResourceType="SomeNameSpace1.Properties.ExceptionResources,SomeProject1"
                exceptionMessageResourceName="errArgumentValidationException"
                faultContractType="SomeNameSpace2.SomeFaultContract, SomeProject2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Fault Contract Exception Handler">
                <mappings>
                  <add source="{Message}" name="FaultMessage" />
                  <add source="{Guid}" name="FaultID" />
                </mappings>
              </add>
            </exceptionHandlers>
          </add>
          <add name="SecurityAccessDeniedException" type="System.ServiceModel.Security.SecurityAccessDeniedException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add name="Logging Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0"
                logCategory="ExceptionLogging" eventId="100" severity="Warning"
                title="Enterprise Library Exception Handling" formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
                priority="0" />
              <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null"
                exceptionMessageResourceType="SomeNameSpace1.Properties.ExceptionResources,SomeProject1"
                exceptionMessageResourceName="errSecurityAccessDeniedException"
                faultContractType="SomeNameSpace2.SomeFaultContract, SomeProject2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Fault Contract Exception Handler">
                <mappings>
                  <add source="{Message}" name="FaultMessage" />
                  <add source="{Guid}" name="FaultID" />
                </mappings>
              </add>
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
    </exceptionPolicies>
  </exceptionHandling>
Nov 30, 2012 at 9:48 AM

In terms of localizing the fault message, note that this is sent back as the faultstring and not in the detail.  Not sure if that is what you want because you are also mapping the exception message itself to FaultMessage in the detail which would not be localized.  

For the second issue if you are seeing a plain FaultException returned on the client side then that usually indicates that an error occurred on the server side and that exception shielding is not working correctly (e.g. Policy name not found or some other type of error).  What you should see on the client side is FaultException<T> returned.  So in your case it would be FaultException<SomeFaultContract>.  You could use WCF tracing or debug the service to get more insight into what is happening

I looked at the ServiceExceptionPolicy and it seems to be working fine.  You do have multiple scenarios in the configuration so maybe if you could narrow down what the scenario is or post a complete example (with code).

Here is what the returned XML would look like:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
  <s:Body>
    <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring xml:lang="en-US">Error Message From Resource File</faultstring>
      <detail>
        <SomeFaultContract xmlns="http://schemas.datacontract.org/2004/07/SomeProject2" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
          <FaultID>5bcbaf56-d3e9-4b38-852c-22e43a815bc1</FaultID>
          <FaultMessage>OOPS!</FaultMessage>
        </SomeFaultContract>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Dec 5, 2012 at 6:20 AM

Sorry for a late reply.

We were able to log the content of exceptions with logging handler and wcf tracing.
What we want to achive is shield the exception detail and present some custom messages to the user. Those messages should indicate what's wrong, such as "access denied" or  "hour shall be between 0 and 23".
We were trying to achive this by adding handlers with different messages for different exception types that we can catch when not using exception shielding.
The exceptions thrown in our service implementation go well with this approach, but those thrown by other code do not.
The WCF service has username and password security. While not using exception shielding, we can catch a SecurityAccessDeniedException at the client side  if the access is not authenticated. With exception shielding, the exception is not dealt as SecurityAccessDeniedException as the third config entry, but as the base Exception as the first config entry. We debugged in the logging handler and found that it was logging a FaultException, not a SecurityAccessDeniedException.
Is it wrong or by design? If by design, how can we distinct them?

Dec 5, 2012 at 6:58 AM

When you shield the exception you usually do not return the exception information.  WCF will translate any unhandled exceptions into a FaultException.  In your posted example you are shielding exceptions and returning a FaultException<SomeFaultContract> to the caller.  This seems to be working fine for me.  Perhaps you can provide a sample project that demonstrates what you are seeing/what you would like to see.  

If you want to return different "exceptions" to the client via WCF you would usually define multiple FaultContracts on your service and you could then map exceptions to these FaultContracts.  For example:

[ServiceContract]
public interface IService
{
    [OperationContract]
    [FaultContract(typeof(ValidationFault))]
    [FaultContract(typeof(SystemFault))]
    [FaultContract(typeof(SecurityFault))]
    void Update(Data data);
}
--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com  

Dec 5, 2012 at 8:18 AM

We don't want the user to see the original exception message or detail, only a single string of different message is needed.

For example, the user shall see "username or password is wrong, access denied" when failed to log on, not a "server side error".

If the example is really needed, we will provide it in a day or two.

Dec 6, 2012 at 4:45 PM

I'm not 100% sure what your scenario is but it sounds like it has something to do with security exceptions (is it only security exceptions? Are these thrown by the service code or by WCF or somewhere else?). Can you provide more information about the exact scenario?  An example would really help.

 --
Randy Levy
Enterprise Library support engineer
entlib.support@live.com  

Dec 17, 2012 at 10:48 AM

Since there is no attachment upload function in the discussion, I sent the example to your email.

Looking forward for your reply.

Dec 17, 2012 at 4:14 PM

Based on the code you sent there are 2 scenarios you are concerned about:

  1. Authorization failed using a custom ServiceAuthorizationManager
  2. Authentication failed using a custom UserNamePasswordValidator

While Exception Shielding can be used to control return messages the original intent is to help "prevent a Web service from disclosing information about the internal implementation of the service when an exception occurs".  By internal implementation this means the application code.  In your case you are trying to shield WCF operations that are external to the actual application service.  Exception Shielding is implemented using WCF extension points so whether you can achieve what you want is, to a large extent, dependent on the implementation, design, and architecture of WCF.

In the first scenario, authorization using a custom ServiceAuthorizationManager, the exception shielding behavior executes and shields the exception but shields the exception as a base Exception and not a security exception.  This is because WCF is throwing a System.ServiceModel.FaultException with a Message of "Access is denied." One thing you could do would be to add a new handler for FaultException and map it to "Message for security access denied":

          <add name="FaultException" type="System.ServiceModel.FaultException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.505.0, Culture=neutral, PublicKeyToken=null"
                exceptionMessage="Message for security access denied"
                faultContractType="WCFServer.HmcFaultContract, WCFServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Fault Contract Exception Handler" />

Of course, it's probably not correct to map all FaultException's to access denied since there could be a variety of reasons for a FaultException to occur.  To handle that scenario it's probably best to create a custom exception handler that will do set the Message text appropriately based on the type and the Message (e.g. "Access is denied.").

The second case does not have an easy solution because the authentication is executing and not allowing the shielding behavior to run.  In this case you have 2 choices: move authentication somewhere further in pipeline (e.g. inside your service), or return your own FaultException (which surfaces to the client as a MessageSecurityException).

The first scenario would be to throw a FaultException from the custom UserNamePasswordValidator:

    /// <summary>
    /// validate UserName and Password, not only used for WSHttp
    /// </summary>
    public class WSHttpPasswordValidate : UserNamePasswordValidator
    {
        /// <summary>
        /// validate UserName and Password
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        public override void Validate(string userName, string password)
        {
            if (userName == null || password == null)
                throw new FaultException("Message for security access denied.", new FaultCode("AccessDenied"));            

            if (userName != "Test" || password != "Password")
                throw new FaultException("Message for security access denied.", new FaultCode("AccessDenied"));
        }
    }

Then in the client you would have to catch a MessageSecurityException:

catch (System.ServiceModel.Security.MessageSecurityException ex)
{
    FaultException fault = ex.InnerException as FaultException;

    if (fault != null)
    {
        if (fault.Code.Name == "AccessDenied")
        {
            Console.WriteLine(fault.Message); // Message for security access denied.
        }
    }
    else
    {
        throw;
    }
}

I see your example uses netTCP binding so I assume you own both sides of the wire.  If that is the case then you could implement custom proxy logic or perhaps a client behavior to hide this from service consumers.  An alternative might be to directly serialize the FaultContract type into the FaultException and then deserialize (since you are using netTCP binding).

--
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to 

Dec 20, 2012 at 11:14 AM

I will try to implement a custom exception handler for the Authorization problem as you suggested.

 

As for the Authentication problem, in fact we have two endpoints with different contract hosted in one windows service with the same security routine, one of them is netTcp for our client, while another is wshttp, to be called by other systems(mostly java and .net).

In this case, shall I throw a FaultException or a generic FaultException<SomeFaultContract> in the Authentication scenario you mentioned?

 

"An alternative might be to directly serialize the FaultContract type into the FaultException and then deserialize (since you are using netTCP binding)."

Do you mean put a FaultContract in the function return value or ref parameter?