如何处理/解析使用WebClient调用的WCF Rest的故障

cro*_*sek 5 rest wcf client exception fault

我有兴趣在WCF REST服务客户端中正确处理故障.在使用任何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?
      }
   }
Run Code Online (Sandbox Code Playgroud)

我可以在Fiddler中看到我收到格式良好的XML"Fault"消息(默认是因为includeExceptionDetailInFaults = true,或者是通过IErrorHandler :: ProvideFault的自定义错误).但是,仅抛出500个内部错误WebException.

我宁愿在客户端上抛出一个FaultException,或者至少能够解析Fault.我们没有使用"服务参考",因此没有代理(如果有更好的方法为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;
}
Run Code Online (Sandbox Code Playgroud)

Phi*_*rdt 6

故障是SOAP协议的一部分,在REST方案中不可用.我不相信任何WCF基础设施都支持开箱即用的功能.

您可以在WebHttp行为配置中设置FaultExceptionEnabled = true以获取FaultException而不是500错误.

但是,您也可以这样做(我在某些测试场景中已经这样做了).这种方法依赖于提前知道故障中预期的FaultDetail类型.

        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;
        }
Run Code Online (Sandbox Code Playgroud)