2012-01-12 10 views
5

Estoy interesado en manejar correctamente las fallas dentro de un cliente de WCF REST Service. Durante el uso de cualquiera de cliente Web, WebRequest, o HttpWebRequest así:Cómo manejar/analizar Fallas para un repositorio de WCF llamado usando WebClient

try 
    { 
     HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri); 
     req.Method = "GET"; 
     HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); 
     // ...process... 
    } 
    catch (WebException wex) 
    { 
     string exMessage = wex.Message; 
     if (wex.Response != null) 
     { 
     using (StreamReader r = new StreamReader(wex.Response.GetResponseStream())) 
      exMessage = r.ReadToEnd(); 

     // the fault xml is available here, really need to parse? and how? 
     } 
    } 

que puedo ver en Fiddler que estoy recibiendo un mensaje de "Fallo" (ya sea el predeterminado XML con un formato agradable porque includeExceptionDetailInFaults = true, o un fallo de encargo a través de IErrorHandler :: ProvideFault). Sin embargo, solo se lanza un error interno de 500 WebException.

Prefiero obtener una FaultException lanzada en el cliente o al menos poder analizar la falla. No usamos "Referencia de servicio", por lo que no hay proxy (corrígeme si hay una forma mejor de hacerlo para un cliente REST WCF). ¿Existe una forma genérica de analizar ese error independientemente de su tipo real T (FaultException) o incluso para un tipo específico como punto de partida? ¡Gracias!

Sobre la respuesta de degorolls:

public SomeContract ThrowErrorTest() 
{ 
    try 
    { 
     return TryCatchExtractAndRethrowFaults<SomeContract>(() => 
     { 
      // Call web service using WebClient, HttpWebRequest, etc. 
      return SomeContract; 
     });     
    } 
    catch (FaultException<CustomFault> fexCustom) 
    { 
     Dbg.WriteLine(fexCustom.Message); 
    } 
    catch (FaultException fex) 
    { 
     Dbg.WriteLine(fex.Message); 
    } 
    catch (WebException wex) 
    { 
     Dbg.WriteLine(wex.Message); 
    } 
    catch (Exception ex) 
    { 
     Dbg.WriteLine(ex.Message); 
    } 
    return null; 
}   

static public T TryCatchExtractAndRethrowFaults<T>(Func<T> doWebRequest) 
{ 
    try 
    { 
     return doWebRequest(); 
    } 
    catch (WebException wex) 
    { 
     FaultException fe = ConvertWebExceptionIntoFault(wex); 
     if (fe != null) 
      throw fe; 
     throw;  // not a fault, just re-throw 
    } 
} 

static protected FaultException ConvertWebExceptionIntoFault(WebException wex) 
{ 
    if (wex.Response == null) 
     return null; 

    XmlDictionaryReader xdr = XmlDictionaryReader.CreateTextReader(
     wex.Response.GetResponseStream(), 
     new XmlDictionaryReaderQuotas()); 

    Message msg = Message.CreateMessage(MessageVersion.None, "ParseFaultException", xdr); 

    // If the start element of the message is "Fault" convert it into a FaultException 
    // 
    using (MessageBuffer msgBuffer = msg.CreateBufferedCopy(65536)) 
     using (Message msgCopy = msgBuffer.CreateMessage()) 
      using (XmlDictionaryReader reader = msgCopy.GetReaderAtBodyContents()) 
       if (reader.IsStartElement("Fault")) 
       { 
        // Must make a copy for the converter 
        msg.Close(); 
        msg = msgBuffer.CreateMessage(); 
        return ConvertMessageToFault(msg); 
       } 

    return null; 
} 

static FaultException ConvertMessageToFault(Message msg) 
{ 
    EnvelopeVersion ev = msg.Version.Envelope; 
    var fault = MessageFault.CreateFault(msg, 65536); 

    if (fault.HasDetail) 
    { 
     string faultName = fault.GetReaderAtDetailContents().Name; 
     switch (faultName) 
     { 
      case "ExceptionDetail": // handle the default WCF generated fault 
       ExceptionDetail exDetail = fault.GetDetail<ExceptionDetail>(); 
       return new FaultException<ExceptionDetail>(exDetail, fault.Reason, fault.Code); 

      case "CustomFault":  // handle custom faults 
       CustomFault cstmDetail = fault.GetDetail<CustomFault>(); 
       return new FaultException<CustomFault>(cstmDetail, fault.Reason, fault.Code); 

      default: 
       throw new Exception("Unrecognized fault detail '" + faultName + 
            "' while re-constructing fault."); 
     } 
    } 
    return null; 
} 

Respuesta

6

fallas son parte del protocolo SOAP y no están disponibles en los escenarios de descanso. No creo que ninguna de las infraestructuras de WCF sea compatible con lo que está haciendo fuera de la caja.

Puede establecer FaultExceptionEnabled = true en su configuración de comportamiento WebHttp para obtener una excepción FaultException en lugar de 500.

Sin embargo, también puede hacer algo como esto (esto lo he hecho en algunos escenarios de prueba). Este método se basa en saber de antemano qué tipo de FaultDetail esperar en la falla.

 bool isFault; 
     if (message.Version == MessageVersion.None) 
     { 
      //Need to determine for ourselves if this is a fault; 
      using (MessageBuffer buffer = message.CreateBufferedCopy(65536)) 
      { 
       message.Close(); 
       message = buffer.CreateMessage(); 
       using (Message message2 = buffer.CreateMessage()) 
       { 
        using (XmlDictionaryReader reader = message2.GetReaderAtBodyContents()) 
        { 
         isFault = reader.IsStartElement("Fault", "http://schemas.microsoft.com/ws/2005/05/envelope/none"); 
        } 
       } 
      } 
     } 
     else 
     { 
      // For SOAP messages this is done for us 
      isFault = message.IsFault; 
     } 

     if (isFault) 
     { 
      var fault = MessageFault.CreateFault(message, 65536); 
      MyServiceFault detail = null; 
      if (fault.HasDetail) 
      { 
       // The only thing we can possible have as detail is an MyServiceFault 
       detail = fault.GetDetail<MyServiceFault>(); 
      } 
      FaultException ex = new FaultException<MyServiceFault>(detail, fault.Reason, fault.Code); 
      throw ex; 
     } 
+0

Parece que eso me acerca. En MessageFault.CreateFault() recibo una queja innerException sobre el espacio de nombres que no es ".../soap-envelope" en lugar de ".../envelope/none". ¿Conoces una buena forma de solucionar el problema del espacio de nombres? Realmente proviene del servidor como ".../envolvente/ninguno". – crokusek

+0

Cambié el argumento a Message.CreateMessage() a MessageVersion.None en lugar de MessageVersion.Default y eso resolvió el problema del espacio de nombres. Como empiezo con una WebException, terminé creando un mensaje desde la secuencia wex.Response. Publicaremos lo que terminó funcionando al final de la pregunta. – crokusek

+0

¿Obtiene WebException incluso después de establecer FaultExceptionEnabled = true? –

Cuestiones relacionadas