2012-01-17 21 views
5

Tengo un servicio web asp.NET (no WCF pero el .asmx clásico con WebMethods) que arroja excepciones. Las excepciones todas derivan de una de las dos clases de excepción de base (ambos de los cuales se derivan de Excepción):¿Cómo puedo determinar el tipo de excepción lanzada por un servicio web asp.net?

public class InputException : Exception 
{ 
    .... 
} 

public class FatalException : Exception 
{ 
    .... 
} 

public class NoFilesFound: FatalException 
{ 
    .... 
} 

.... 

El servicio web en este momento inicia la excepción, según sea necesario. En mi código de cliente que puedo detectar las excepciones y veo el mensaje como este:

Server was unable to process request. ---> There were no files found

Sin embargo, la excepción es de tipo FaultException (como se ve cuando lo haga .GetType() en la excepción capturada). El cliente que realiza la llamada necesita poder diferenciar entre una InputException y una FatalException (y diferenciar de manera óptima entre las derivadas individuales, pero eso no es tan importante). En este momento, la única forma de hacerlo es analizar el mensaje, quitar el texto antes de "--->" y activar el texto. Eso claramente no es óptimo.

Sé que puedo lanzar SoapExceptions con Código personalizado, pero quiero evitar eso si es posible. Además, parece diseñado para aquellos que se ocupan de XML, pero todo nuestro código de servicio web no toca XML, ya que ya está deserializado para nosotros.

En resumen, ¿hay alguna manera de lanzar excepciones personalizadas desde el servicio web y para que el cliente llamante pueda diferenciar entre las excepciones?

Respuesta

7

La forma correcta de manejar esto es usar un SoapException. Así que básicamente atrapará todas las excepciones posibles en el servicio y luego las traducirá a SoapException (la documentación a la que he vinculado contiene un ejemplo) y luego, en lugar de arrojar alguna excepción personalizada, lanzará SoapException e incluirá la información que le interese. el nodo XML detallado de la falla.

Luego, al invocar el servicio en el cliente, podrá capturar esta SoapException y analizar el nodo Detail XML para reunir más detalles sobre el motivo exacto de la falla que ocurrió en el servidor.

Toda esta generación manual de nodos de falla se ha vuelto muy fácil en WCF donde simplemente trabaja con contratos de datos y captura FaultException<SomeFaultContract>.

Tomemos un ejemplo. Supongamos que tenemos el siguiente método de servicio que arroja un SoapException y proporciona detalles acerca del error en el nodo Detalles:

[WebMethod] 
public string HelloWorld() 
{ 
    var doc = new XmlDocument(); 
    var node = doc.CreateNode(
     XmlNodeType.Element, 
     SoapException.DetailElementName.Name, 
     SoapException.DetailElementName.Namespace 
    ); 
    // you could actually use any sub nodes here 
    // and pass even complex objects 
    node.InnerText = "no files found"; 

    throw new SoapException(
     "Fault occurred", 
     SoapException.ClientFaultCode, 
     Context.Request.Url.AbsoluteUri, 
     node 
    ); 
} 

al consumirlo se puede coger para este SoapException en el cliente:

using (var client = new WebService1()) 
{ 
    try 
    { 
     var result = client.HelloWorld(); 
    } 
    catch (SoapException ex) 
    { 
     var detail = ex.Detail; 
     // detail.InnerText will contain the detail message 
     // as detail is an XmlNode if on the server you have 
     // provided a complex XML you would be able to fetch it here 
    } 
} 

Como sus excepciones InputException, FatalException y NoFilesFound se refieren, están bien, pero deben permanecer en el servidor. Atrápalos en tu método web y crea un SoapException adecuado que el cliente pueda dar sentido al consumir el servicio.

+0

¡Gracias! Me pregunto si puedo hacer que mis clases personalizadas se deriven de SoapException y luego use el constructor para construir los XmlNodes ... – user1079591

+0

@ user1079591, sí, puedes y en realidad es una muy buena forma de proceder. –

+0

Así que hice lo que sugirió, pero en el código del cliente no lo está captando como SoapException, sino más bien como System.ServiceModel.FaultException. En mi código tengo dos bloques catch: uno para SoapException y otro para Exception regular y siempre va a la Exception regular y cuando hago a.GetType() en el genérico dice que es de tipo System.ServiceModel.FaultException. – user1079591

Cuestiones relacionadas