10.03.2010

Reportando Excepciones al Cliente en WCF

Uno de los problemas más comúnes a la hora de trabajar con WCF es el manejo de excepciones del lado del servicio. Es normal ver como en la implementación del servicio, el desarrollador pone un manejo de excepciones normal, basado en su conocimiento del uso de las mismas en el desarrollo de sus aplicaciones en el framework; sin embargo, a la hora de reportar la excepción el resultado no es el esperado, ya que la excepción no llega al cliente, si no mas bien llega una excepción al cliente como la que se muestra a continuación:

The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.

The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.

Server stack trace:

   at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)

   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)

   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)

   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)

   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)

   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:

   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)

   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)

   at IAuthenticationService.AuthenticateUser(WcfUser user)

   at AuthenticationServiceClient.AuthenticateUser(WcfUser user)

La excepción anterior fue lanzada por un servicio que simplemente lanza una excepción tal y como se puede ver en el siguiente código

public class AuthenticationService : IAuthenticationService
{
public bool AuthenticateUser(WcfUser user)
{
throw new Exception("Invalid Action");
}
}



Claramente el servicio encapsula la excepción del lado del cliente por muchas razones entre ellas seguridad. Cuando alguien quiere hackear nuestros servicios, una de las cosas que hace es invocar servicios y provocar que estos fallen para poder ver en la información que retornan las excepciones y así poder tener pistas de como “violar la seguridad” del servidor donde se hospedan nuestros servicios. Es por esto que pese a que es posible “transportar el error” al lado del cliente en WCF utilizando la directiva IncludeExceptionDetailsInFaults no es recomendable hacerlo cuando la aplicación sale a producción.



Para reproducir el error anterior basta con ejecutar el servicio en el WCF Test Client y podemos ver que aunque sabemos el tipo de excepción que vamos a lanzar, la misma nunca llega a propagarse hasta donde esta el cliente.



image



La Solución



El escenario anterior se da porque WCF fue diseñado con la interoperabilidad en mente y por esta razón utiliza SOAP Faults para manejar los errores en lugar de expceción. Para poder ser interoperables, los SOAP Fault Messages son mucho más simples que las expecpciones. Los SOAP Fault Messages tiene dos elements:





  • Code: El cual provee la información del error para comprensión a nivel de máquina – es decir, un código de error natural.




  • Reason: La misma información del error pero legible para un humano.




Si se requiere agregar información adicional acerca del error, el servicio va a tener que definir un Fault Data Contract que puede ser consumido por el cliente.



En el código siguiente se puede ver el servicio anterior pero utilizando SOAP Fault Messages.





using System.ServiceModel;

namespace WFCErrorManagment
{
public class AuthenticationService : IAuthenticationService
{
public bool AuthenticateUser(WcfUser user)
{
throw new FaultException(new FaultReason("Error Autenticando el Usuario"),
new FaultCode("Error Autenticación"));
}
}
}


A diferencia del código anterior, en este caso estamos usando la clase FaultException la cual propaga el error de forma transparente desde el servidor hasta el cliente tal y como se puede ver en la siguiente figura:



image



En el siguiente post vamos a explorar los contratos de error personalizados en WCF.



Technorati Tags: ,,

No hay comentarios: