2012-01-12 4 views
5

Sono interessato a gestire correttamente i guasti all'interno di un client di servizio REST WCF. Durante l'utilizzo di uno qualsiasi dei WebClient, WebRequest, o HttpWebRequest in questo modo:Come gestire/analizzare i guasti per un WCF Rest chiamato 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? 
     } 
    } 

posso vedere in Fiddler che sto ottenendo un messaggio di "Guasto" (sia il default XML ben formattata perché includeExceptionDetailInFaults = true, o un guasto personalizzato tramite IErrorHandler :: ProvideFault). Tuttavia, viene lanciata solo una 500 Errore interno WebException.

Preferisco ottenere una FaultException lanciata sul client o almeno essere in grado di analizzare l'errore. Non stiamo usando "Service reference" quindi non c'è proxy (correggimi se esiste un modo migliore per farlo per un client WCF REST). Esiste un modo generico per analizzare quell'errore indipendentemente dal suo tipo effettivo T (FaultException) o anche per tipo specifico come punto di partenza? Grazie!

Sulla risposta da 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; 
} 

risposta

6

difetti sono parte di protocollo SOAP e non sono disponibili in scenari REST. Non credo che nessuna delle infrastrutture WCF supporti ciò che state facendo fuori dalla scatola.

È possibile impostare FaultExceptionEnabled = true nella configurazione del comportamento WebHttp per ottenere un errore FaultException anziché 500.

Tuttavia, è possibile anche fare qualcosa di simile (l'ho fatto in alcuni scenari di test). Questo metodo si basa sulla conoscenza anticipata del tipo di errore che FaultDetail deve aspettarsi nell'errore.

 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

Questo sembra avvicinarmi. Su MessageFault.CreateFault() ricevo un reclamo di innerException sullo spazio dei nomi che non è ".../soap-envelope" invece di ".../envelope/none". Sei a conoscenza di un buon modo per aggirare il problema dello spazio dei nomi? Viene davvero dal server come ".../envelope/none". – crokusek

+0

Ho modificato l'argomento in Message.CreateMessage() in MessageVersion.None anziché MessageVersion.Default e ho risolto il problema dello spazio dei nomi. Poiché sto iniziando con una WebException, ho finito per creare un messaggio dal flusso wex.Response. Pubblicherà ciò che ha finito per funzionare alla fine della domanda. – crokusek

+0

Ricevi WebException anche dopo aver impostato FaultExceptionEnabled = true? –

Problemi correlati