domingo, 23 de noviembre de 2008

Manejo de Excepciones en Web Services (SOAP Exceptions) con .Net

Por segunda vez :-( , me toca renegar un poco con el manejo de excpecion que son lanzadas desde web services estándares (no WCF) y hago este post con el solo fin de recordar cómo era para la próxima vez que me toque y por si a alguien más pudiera servirle.

Como sabrán las excepciones lanzadas desde un servicio web (SOAP Exceptions) tienen la particularidad que la propiedad InnerException llega al cliente que consume el servicio por lo general (siempre en mi caso) vacía. Existe sin embargo, en este tipo particular de excepción, una propiedad llamada ¨Detail¨ la cual es perfectamente serializable como XML y puede ser enviada al cliente para su manejo sin ningún tipo de inconveniente.

Ahora bien, para ser lo más explicativo posible, voy a partir del supuesto de que nuestra solución esta desarrollada en distintas capas y la excepción no se genera siempre en el WebMethod que consume el cliente que deberá manejar esta excepción. Pues bien, vamos a suponer que en el caso de nuestra capa de servicios ocurre una excepción y es lanzada hacia alguna capa superior:



public void lanzarException()

     {

         try

         {

             throw new Exception("Se produjo la exception que podria haber sido SQL o regla de negocio");

         }

         catch (Exception e)

         {

             throw e;



         }




     }



Una vez capturada esta excepción, el paso siguiente seria extraer la información de la misma y darle el formato correcto para poder insertar la información extraída, insertarla en un XML y luego "copiar" el mismo en la propiedad detail de la SoapException queremos informar. Para un fin práctico es puesto el código de esta funcionalidad en un método estático (no necesita crearse una instancia de la clase que lo contiene). Es conveniente también que la clase que contiene este metodo este en el mismo proyecto que los servicios web que estamos publicando (Carpeta App_Code) para así poder ser llamada desde cualquier parte del proyecto web.
La funcionalidad de dicho método debería ser como sigue:


public static SoapException BuildSoapException(string infoInnerNode)

        {

            // Nodo con detalles de excepcion.

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

            System.Xml.XmlNode node = doc.CreateNode(XmlNodeType.Element,

                 SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace);

            //Genero informacion especifica para una soap Exception

            //Agrago el child nodo para el XML

            System.Xml.XmlNode details =

              doc.CreateNode(XmlNodeType.Element, "NodoInformacion",

                             "http://tempuri.org/");

            System.Xml.XmlNode detailsChild =

              doc.CreateNode(XmlNodeType.Element, "childConSpecialInfo",

                             "http://tempuri.org/");

            details.InnerText = infoInnerNode;

            details.AppendChild(detailsChild);



            // Meto el child adentro del nodo que acabo de crear.

            node.AppendChild(details);



            //Lanzo la excepcion     

            //el parametro SoapException.ClientFaultCode indica que la llamada desde el cliente fue incorrecta 

            //en formato o parametros

            SoapException se = new SoapException("Error de parametros,  formato, datos desde el cliente",

              SoapException.ClientFaultCode,

              "SeviciosContosoGetProductos",

              node);

            return se;



        }



Y suponiendo que la clase que contiene a dicho método se llamara SOAPExceptionBuilder podríamos simplificar el catch de nuestros web methods de la siguiente forma:


  catch (Exception e)
        {/* Este es el catch a poner el los WebMethods*/

            SOAPExceptionBuilder.exceptionALanzar = SOAPExceptionBuilder.BuildSoapException(e.Message);
            throw SOAPExceptionBuilder.exceptionALanzar;
        }


Finalizada la parte de servidor de cómo construir una Soap Exception solo restaría ver el trato que deberíamos darle del lado del cliente una vez que esta excepción fue capturada, pues bien, como decíamos al principio la propiedad detail de nuestra excepción no contiene otra cosa que no sea un XML por lo que podríamos recorrerlo y mostrar los detalles del error, crear una nueva excepción con los datos obtenidos o simplemente loguearlos. Una de las formas de recorrerlo sería la siguiente (yo solo los muestro por pantalla pero podría ser cualquier de las opciones anteriores):


 catch (SoapException error)
        {

            System.Xml.XmlNode node = error.Detail;
            string s = "";

            foreach (System.Xml.XmlNode nod in node.ChildNodes)
            {

                 s = nod.InnerText;
          
            }
            // LLeno los controles con los detalles de la exception
      
            TextBox1.Text = "Fault Code Namespace : " + error.Code.Namespace;
            TextBox2.Text = "SOAP Actor que lanzo la exception : " + error.Actor;
            TextBox3.Text = "Detalles : " + s;
            TextBox4.Text = "Mensaje : " + error.Message;


            return;
        }


Eso seria todo, espero que sirva y acordarme donde lo puse la proxima que lo tenga que usar :-)

Adjunto el código correspondiente a una solución en la que se encuentra un proyecto con "lógica" para lanzar una excepción cualquiera, un servicio que instancia esta clase y un proyecto web con una página que consume dicho servicio con todo el código utilizado en el ejemplo.

Ariel Serlin


Para bajar el codigo hace click aqui:

3 comentarios:

big dijo...

Excelente articulo, muchas gracias

big dijo...

Excelente, muchas gracias

Ariel Serlin dijo...

Por nada! Me alegro que te haya sido util