Как обрабатывать/разбирать Faults для WCF Rest вызываемого с помощью WebClient

Меня интересует правильная обработка Faults внутри клиента WCF REST Service. При использовании любого из WebClient, WebRequest или HttpWebRequest, например, так:

   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?
      }
   }

Я могу видеть в Fiddler, что я получаю хорошо отформатированное XML сообщение "Fault" (либо по умолчанию, поскольку includeExceptionDetailInFaults=true, либо пользовательское сообщение через IErrorHandler::ProvideFault). Однако выбрасывается только WebException с внутренней ошибкой 500.

Я бы предпочел получить FaultException, брошенный на клиенте, или, по крайней мере, иметь возможность разобрать ошибку. Мы не используем "Service reference", поэтому нет прокси (пожалуйста, поправьте меня, если есть лучший способ сделать это для REST WCF клиента). Есть ли общий способ разобрать эту ошибку независимо от ее фактического типа T (FaultException) или даже для определенного типа в качестве отправной точки? Спасибо!

В развитие ответа от 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;
}
5
задан crokusek 12 January 2012 в 22:39
поделиться