2

我刚开始使用 WCF,并试图验证支持 JSON 和 XML 的 WCF 休息服务中的故障处理。我的测试服务产生了一个故障,但无论我尝试什么,我都无法让我的客户获取故障的详细信息(并且行为因请求格式和 http 状态代码而异):

我的测试服务产生的故障如下:

public Data GetResponse()
{
    throw new WebFaultException<ErrorDetails>(
        new ErrorDetails {ErrorMessage = "Server Config Value not set"},
        HttpStatusCode.OK
        );
}

这相当合理地通过网络发送:

{"ErrorMessage":"Server Config Value not set"}

和:

<ErrorDetails xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <ErrorMessage>Server Config Value not set</ErrorMessage>
</ErrorDetails>

我的客户定义为FaultContract

[OperationContract]
[WebInvoke(
    UriTemplate="/response",
    Method="GET",
    RequestFormat = WebMessageFormat.Xml, // or .Json
    ResponseFormat = WebMessageFormat.Xml // or .Json
    )]
[FaultContract(typeof(ErrorDetails), Namespace="")]
Data GetResponse();

这是(格式/状态代码)的完整错误消息:

XML/冲突:

请求响应 CommunicationException:远程服务器返回意外响应:(409) Conflict., System.Collections.ListDictionary Internal, System.ServiceModel.ProtocolException Press a key to exit...

和 XML/OK:

请求响应异常:无法使用 DataContractSerializer 反序列化具有根名称“ErrorDetails”和根命名空间“”的 XML 主体(用于操作“GetResponse”和合同(“IClient”,“”))。确保将XML对应的类型添加到服务的know n types集合中。,System.Collections.ListDictionaryInternal Press a key to exit...

和 JSON/冲突:

请求响应 CommunicationException:远程服务器返回意外响应:(409) Conflict., System.Collections.ListDictionary Internal, System.ServiceModel.ProtocolException Press a key to exit...

和 JSON/OK:

请求响应 响应:请求完成 按某个键退出...

客户端代码以正确的顺序捕获异常:

try
{
    Console.WriteLine("Requesting response");
    Console.WriteLine("Response: " + client.GetResponse().Message);
    Console.WriteLine("Request complete");
}
// sanity check, just in case...
catch (WebFaultException<ErrorDetails> ex)
{
    Console.WriteLine("WebFaultException<ErrorDetails>: " + ex.Detail.ErrorMessage + ", " + ex.Reason);
}
catch (FaultException<ErrorDetails> ex)
{
    Console.WriteLine("FaultException<ErrorDetails>: " + ex.Detail.ErrorMessage + ", " + ex.Reason);
}
catch (FaultException ex)
{
    Console.WriteLine("FaultException: " + ex.Message + ", " + ex.Reason);
}
catch (CommunicationException ex)
{
    Console.WriteLine("CommunicationException: " + ex.Message + ", " + ex.Data + ", " + ex.GetType().FullName);
}
catch (Exception ex)
{
    Console.WriteLine("Exception: " + ex.Message + ", " + ex.Data);
}

我必须做什么才能FaultException<ErrorDetails>被抛出并且我可以访问ErrorDetails?

注意:要点应该是完全可编译和可运行的。

4

2 回答 2

4

在 WCF REST 客户端(从 WCF 4.0 版开始)服务中,不能使用 Faults/FaultContract 作为错误处理机制:

WCF 列表中的每个线程:

在 WCF Rest 服务中没有 SOAP 消息,因此您无法向客户端返回 FaultException。实际上,适当的状态码作为 HTTP 标头返回给请求者,允许请求者确定调用的结果。

并来自StackOverflow 帖子

故障是 SOAP 协议的一部分,在 REST 场景中不可用

考虑的选项

FaultContract -FaultContractAttribute适用于 SOAP 信封,但不适用于 WCF REST 服务。如果您通过格式化为 SOAP 信封的格式发送 XML,您可以让它工作,但是您没有使用合理的自定义错误消息。

IErrorHandler - 快速阅读文档表明这是针对服务的,并认为这是一个新手错误:“允许实现者控制返回给调用者的错误消息,并可选择执行自定义错误处理,例如日志记录。”

消息检查器-AfterReceiveRequest不执行,因为CommunicationException首先抛出 。

解决方法

经过一番挖掘后,我发现一位博主建议创建一个帮助类来从嵌入在异常对象中的响应流中提取信息。这是他的实现,原样:

public static void HandleRestServiceError(Exception exception, Action<TServiceResult> serviceResultHandler, Action<TServiceFault> serviceFaultHandler = null, Action<Exception> exceptionHandler = null)
{
  var serviceResultOrServiceFaultHandled = false;

  if (exception == null) throw new ArgumentNullException("exception");
  if (serviceResultHandler == null) throw new ArgumentNullException("serviceResultHandler");

  // REST uses the HTTP procol status codes to communicate errors that happens on the service side.
  // This means if we have a teller service and you need to supply username and password to login
  // and you do not supply the password, a possible scenario is that you get a 400 - Bad request.
  // However it is still possible that the expected type is returned so it would have been possible 
  // to process the response - instead it will manifest as a ProtocolException on the client side.
  var protocolException = exception as ProtocolException;
  if (protocolException != null)
  {
    var webException = protocolException.InnerException as WebException;
    if (webException != null)
    {
      var responseStream = webException.Response.GetResponseStream();
      if (responseStream != null)
      {
        try
        {
          // Debugging code to be able to see the reponse in clear text
          //SeeResponseAsClearText(responseStream);

          // Try to deserialize the returned XML to the expected result type (TServiceResult)
          var response = (TServiceResult) GetSerializer(typeof(TServiceResult)).ReadObject(responseStream);
          serviceResultHandler(response);
          serviceResultOrServiceFaultHandled = true;
        }
        catch (SerializationException serializationException)
        {
          // This happens if we try to deserialize the responseStream to type TServiceResult
          // when an error occured on the service side. An service side error serialized object 
          // is not deserializable into a TServiceResult

          // Reset responseStream to beginning and deserialize to a TServiceError instead
          responseStream.Seek(0, SeekOrigin.Begin);

          var serviceFault = (TServiceFault) GetSerializer(typeof(TServiceFault)).ReadObject(responseStream);

          if (serviceFaultHandler != null && serviceFault != null)
          {
            serviceFaultHandler(serviceFault);
            serviceResultOrServiceFaultHandled = true;
          }
          else if (serviceFaultHandler == null && serviceFault != null)
          {
            throw new WcfServiceException<TServiceFault>() { ServiceFault = serviceFault };
          }
        }
      }
    }
  }

  // If we have not handled the serviceResult or the serviceFault then we have to pass it on to the exceptionHandler delegate
  if (!serviceResultOrServiceFaultHandled && exceptionHandler != null)
  {
    exceptionHandler(exception);
  }
  else if (!serviceResultOrServiceFaultHandled && exceptionHandler == null)
  {
    // Unable to handle and no exceptionHandler passed in throw exception to be handled at a higher level
    throw exception;
  }
}
于 2012-08-30T22:20:39.573 回答
0

我用过这个:

 try
            {
              //wcf service call
            }
            catch (FaultException ex)
            {
               throw new Exception( (ex as WebFaultException<MyContractApplicationFault>).Detail.MyContractErrorMessage );                
            }
于 2014-02-13T10:50:53.003 回答