CESAR DE LA TOR... 的个人资料Blog de CESAR DE LA TORR...照片日志列表更多 工具 帮助

日志


1月3日

Gestion de Excepciones 'custom' en Servicios Web básicos (ASMX)

La gestión de excepciones en Servicios Web básicos (ASMX) no es igual que la
gestión de excepciones con objetos en el CLR de .NET. Puesto que no es algo muy, muy sencillo, y me lo ha preguntado mucha gente, voy a escribir un posting sobre ello.
Cuando simplemente estás con objetos de .NET (en el CLR), trabajas con
objetos Exception y derivados (SqlException, etc.). Sin embargo, para
gestionar en la aplicación cliente las excepciones originadas en un Servicio
Web básico (ASMX), tienes que trabajar con la clase 'SoapException', con los detalles de la excepción en la propiedad 'Message'.

El problema viene originado porque sin utilizar la clase SoapException, el diccionario 'Data' y la 'innerException' de una excepción normal de .NET (CLR), no se serializan ni se transmiten por lo tanto a la aplicación cliente que consume el Web Service.

Básicamente, como trabajo yo para gestionar las excepciones de servicios Web
a las que quiero acceder desde la aplicación cliente (por ejemplo, una
excepción de negocio que dependiendo de ciertos valores de la excepción
quiero actuar de diferente forma en la aplicación cliente del web service) es:


(1).- Lanzo la excepción de negocio o se produce una cualquiera (p.e.
SqlException)

Ejemplo en C# de creación de excepción en método de componente de negocio:

//(CDLTLL)Si no tiene acceso autorizado, lanzamos una excepción de seguridad.

if (!authorized)

{

string infoCustom = "Lo que quiera poner";

      ApplicationException appEx = new ApplicationException(string.Format(CultureInfo.CurrentCulture, global::Empresa.AplicacionA.Modulo1.ComponentesAplicacion.Properties.Resources.BllEx002, userName, classMethod, requiredPermissionName));

      appEx.Data.Add("ExceptionId", "BllEx002");

      appEx.Data.Add("UserName", userName);

      appEx.Data.Add("InfoCustom", infoCustom);

      throw appEx;

}

Lo importante es que estoy metiendo los datos que yo quiera dentro del objeto Data que está a su vez dentro del objeto Exception.

Si la excepción es causada por el sistema (SqlException, por ejemplo), pues tendrá otra información relativa al error dentro de la excepción.


(2).- Capturo la excepción en el propio Servicio Web, con un try-catch dentro de
los Web-Methods, y ahí convierto la excepción normal de .NET en una
'SoapException'.

[WebMethod]

public bool TransferenciaBfll_RealizarTransferencia(string numCuentaOrigen, string numCuentaDestino, int cantidad)

{

  try

  {               

      return TransferenciaBfll.RealizarTransferencia(numCuentaOrigen,

                       numCuentaDestino,

                       cantidad);

  }

  catch (Exception ex)

  {

    ProcesarExcepcionParaSoap(ex);

    return false;

  }

 }

En ese WebMethod solamente se llama a un método de un componente de negocio y en caso de haber alguna excepción, la procesamos en el Catch. ¿Qué hacemos en ese método llamado ProcesarExcepcionParaSoap(ex), pues precisamente convertir la excepción normal de .NET en una SoapException y cambiar los datos internos de unas colecciones de datos a otras.

Aquí tenéis lo que hace este método ProcesarExcepcionParaSoap(ex):

        private void ProcesarExcepcionParaSoap(Exception ex)

        {

            //(CDLTLL) Si tenemos detalles de la excepción de la transferencia

            if (ex.Data["ExceptionId"] != null)

            {

                //(CDLTLL) Hace falta crear la SoapException si se quiere transmitir

                //(CDLTLL) detalles al cliente, porque el diccionario 'Data' no se serializa en XML del WS

                //(CDLTLL) ni tampoco se serializa la innerException.

                //(CDLTLL) Construimos el detalle de la excepción SOAP               

                System.Xml.XmlDocument doc = new System.Xml.XmlDocument();

                System.Xml.XmlNode node = doc.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace); 

                //Construimos detalles específicos de la SoapException.                   

                // Añadimos un nodo hijo XML de detalle, con un atributo por cada propiedad a controlar.

                System.Xml.XmlNode detalles = doc.CreateNode(XmlNodeType.Element, "DetallesExcepcionTransferencia", http://tempuri.org/); 

                XmlAttribute attrExceptionId = doc.CreateAttribute("e", "ExceptionId", "http://tempuri.org/");

                attrExceptionId.Value = ex.Data["ExceptionId"].ToString();

                detalles.Attributes.Append(attrExceptionId);

                if (ex.Data["AccountNum"] != null)

                {

                    XmlAttribute attrAccountNum = doc.CreateAttribute("a", "AccountNum", "http://tempuri.org/");

                    attrAccountNum.Value = ex.Data["AccountNum"].ToString();

                    detalles.Attributes.Append(attrAccountNum);

                } 

                if (ex.Data["UserName"] != null)

                {

                    XmlAttribute attrAccountNum = doc.CreateAttribute("u", "UserName", "http://tempuri.org/");

                    attrAccountNum.Value = ex.Data["UserName"].ToString();

                    detalles.Attributes.Append(attrAccountNum);

                }

                if (ex.Data["ClassMethod"] != null)

                {

                    XmlAttribute attrAccountNum = doc.CreateAttribute("c", "ClassMethod", "http://tempuri.org/");

                    attrAccountNum.Value = ex.Data["ClassMethod"].ToString();

                    detalles.Attributes.Append(attrAccountNum);

                }

                 node.AppendChild(detalles); 

                //Lanzamos la excepción SOAP con DETALLES(DATA).   

                SoapException se = new SoapException(ex.Message, SoapException.ClientFaultCode, Context.Request.Url.AbsoluteUri, node); 

                throw se;

            }

            else

            {

                //Lanzamos la excepción BASICA sin detalles.   

                throw ex;

            }

        }

(3).- Capturas la excepción en la aplicación cliente y accedes a las propiedades
dentro de la propiedad 'Message'.

Más en detalle, en el cliente, lo que hacemos es crear una instancia del objeto proxy y en el caso de detectar alguna excepción, procesarla en el CATCH:

            try

            {

                // Se instancia el WebService del Cliente para realizar la llamada

                Modulo1WS modulo1Ws = new Modulo1WS();               

 

                // Proporcionamos las Credenciales actuales

                modulo1Ws.Credentials = System.Net.CredentialCache.DefaultCredentials;

                return modulo1Ws.TransferenciaBfll_RealizarTransferencia(numCuentaOrigen, numCuentaDestino, cantidad);

            }

            catch (SoapException soapEx)

            {

                ExcepcionesUtil.ProcesarExcepcionSOAPGestionada(soapEx);

                return false;

            }

Aquí también, el trabajo realmente se hace en el método ProcesarExcepcionSOAPGestionada(soapEx). 

Básicamente, dentro de este método se accede a nuestros valores/propiedades de la siguiente forma:

string accNum = soapEx.Detail.FirstChild.Attributes["a:AccountNum"].Value.ToString();

 

Como se puede observar, el procesamiento de excepciones custom para Servicios-Web no es algo inmediato. Tampoco es que sea muy difícil, pero se complica. J