如何使用WebClient调用的WCF Rest处理/解析故障 [英] How to handle/parse Faults for a WCF Rest called using WebClient

查看:108
本文介绍了如何使用WebClient调用的WCF Rest处理/解析故障的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对在WCF REST服务客户端中正确处理错误感兴趣。像这样使用任何WebClient,WebRequest或HttpWebRequest时:

I'm interested in properly handling Faults within a WCF REST Service client. While using any of WebClient, WebRequest, or HttpWebRequest like so:

   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进行的自定义错误)。但是,只会引发500个内部错误WebException。

I can see in Fiddler that I am getting a nicely formatted XML "Fault" message (either the default because includeExceptionDetailInFaults=true, or a custom fault via IErrorHandler::ProvideFault). However, only a 500 Internal error WebException is being thrown.

我宁愿在客户端上抛出FaultException或至少能够解析Fault。我们不使用服务参考,所以没有代理(如果有更好的方法为REST WCF客户端执行此操作,请更正我)。
是否有一种通用方法可以解析该故障,而不管其实际类型T(FaultException)还是特定类型作为起点?谢谢!

I would rather get an FaultException thrown on the client or at least be able to parse the Fault. We are not using "Service reference" so there is no proxy (please correct me if there is a better way to do this for a REST WCF client). Is there a generic way to parse that fault regardless of its actual type T (FaultException) or even for specific type as starting point? Thanks!

基于degorolls的答案:

Building on answer from 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;
}


推荐答案

故障是SOAP协议的一部分并且在REST场景中不可用。我不相信任何WCF基础结构都能支持您正在执行的工作。

Faults are part of SOAP protocol and are not available in REST scenarios. I don't believe any of the WCF infrastructure supports what you are doing out of the box.

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

You can set FaultExceptionEnabled=true in your WebHttp behavior configuration to get a FaultException rather than 500 error.

但是,您也可以执行类似的操作(在某些测试场景中,我已经这样做了)。这种方法依赖于提前知道在故障中期望什么样的FaultDetail。

However, you can also do something like this (I have done this in some testing scenarios). This method relies on knowing ahead of time what kind of FaultDetail to expect in the fault.

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

这篇关于如何使用WebClient调用的WCF Rest处理/解析故障的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆