WCF Exception Shielding using Enterprise Library Exception Block
I'm using the enterprise library to implement exception shielding based on Guy Burstein's blog post here. My implementation is basically identical to his, however I've a couple of problems:
1: The point of exception shielding is to not pass through the exact details of the error, so why does the mapping specify passing the message from the original exception unchanged. What would be a sensible way of providing a less s开发者_如何转开发pecific message?
2: The faultexception that is passed back contains the message from the original exception, so irrespective of what I manage to wrap or return in the fault contract the details are available on the faultexception. Is there a way to remove the information back to the generic "internal error" message.
Note that if I don't use the [ExceptionShielding]
and [FaultContract]
attributes then the service returns the standard "The server was unable to process the request due to an internal error" message.
If you know that you have your own application exceptions that do not reveal sensitive information you can handle those specific exceptions and map the message property. For other exceptions you can skip mapping the Message. In addition, you can specify an exceptionMessage on the fault contract itself:
<add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
postHandlingAction="ThrowNewException" name="Exception">
<exceptionHandlers>
<add exceptionMessage="Oops! A System Error Occurred in the Service Layer." faultContractType="MyTypes.Exceptions.ServiceFault, MyTypes.Exceptions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="Default Fault Contract Handler">
</add>
exceptionMessage
will be populated on the FaultException's Message property. If you don't map the detail then that might be sufficient for you.
If you do want to populate values in your fault class then you could do it in two steps:
- Create a handler(s) to handle the exception(s) you are interested in and sanitize the message.
- Create a handler to create the FaultContract
Using Guy Burstein's WCF Exception Handling with Exception Handling Application Block Integration sample this would be setting the MessageText of the ServiceFault (the rest of this is based on that example).
So your service would look like this:
public int CreateOrder(string currency, double amount)
{
try
{
throw new InvalidOperationException("Cannot call this operation!");
}
catch (Exception e)
{
// This sanitizes the message and throws a new Exception
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionPolicy.
HandleException(e, "SanitizeAndThrow");
}
return 0;
}
Then in configuration you would create a SanitizeAndThrow handler to sanitize the Message:
<add name="SanitizeAndThrow">
<exceptionTypes>
<add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
postHandlingAction="ThrowNewException" name="Exception">
<exceptionHandlers>
<add exceptionMessage="This is a sanitized message."
replaceExceptionType="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
name="Replace Handler" />
</exceptionHandlers>
</add>
</exceptionTypes>
</add>
Then you can use your exception shielding to create a FaultException:
<add name="WCF Exception Shielding">
<exceptionTypes>
<add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="ThrowNewException" name="Exception">
<exceptionHandlers>
<add exceptionMessage="Oops!"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF"
name="DefaultFaultContract Handler"
faultContractType="Bursteg.Samples.WCFIntegration.ServiceContracts.ServiceFault, Bursteg.Samples.WCFIntegration.ServiceContracts">
<mappings>
<add name="Id" source="{Guid}"/>
<add name="MessageText" source="{Message}"/>
</mappings>
</add>
</exceptionHandlers>
</add>
Some notes about this example:
If you don't sanitize the exception then it is possible to leak the message out. This can be mitigated by having the "SanitizeAndThrow" handler throw your own custom exception and have the shielding policy handle your custom type(s) and map the Message but for any other type don't perform any mapping.
This code is not production ready and does not hold to best practices. It is just an example and a starting point. e.g. you don't usually want to catch the general
Exception
, the service code has no NotifyRethrow logic, etc.If need be you can create a custom fault contract handler to handle more complicated scenarios.
Perhaps there is a simpler way to sanitize the
Detail
of the fault exception (e.g. chaining handlers in the exception shielding)?
精彩评论