WCF:FaultContract设置但不导致在REST服务中捕获FaultException <t> </t>

时间:2012-08-18 01:25:38

标签: c# xml json wcf rest

我刚开始使用WCF并且正在尝试验证支持JSON和XML的WCF休息服务中的错误处理。 My test service生成错误,但无论我尝试什么,我都无法获取my client来获取错误的详细信息(并且行为因请求格式和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   内部,System.ServiceModel.ProtocolException   按一个键退出...

和XML / OK:

  

请求回复   例外:无法使用根名称'ErrorDetails'和根命名空间''反序列化XML主体(对于操作'GetResponse'而言   d使用DataContractSerializer的合约('IClient',''))。确保将与XML对应的类型添加到know中   n类型服务的集合。,System.Collections.ListDictionaryInternal   按一个键退出...

和JSON /冲突:

  

请求回复   CommunicationException:远程服务器返回意外响应:(409)Conflict。,System.Collections.ListDictionary   内部,System.ServiceModel.ProtocolException   按一个键退出...

和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

注意:gist应该是完全可编译的并且可以运行。

2 个答案:

答案 0 :(得分:3)

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

WCF列表中的每one thread

  

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

来自StackOverflow post

  

错误是SOAP协议的一部分,在REST方案中不可用

考虑选项

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

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

Message Inspectors - AfterReceiveRequest未执行,因为首先抛出CommunicationException

解决方法

经过大量挖掘后,我found one blogger建议创建一个帮助程序类,以从嵌入在异常对象中的响应流中提取信息。这是他的实现,原样是:

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

答案 1 :(得分:0)

我用过这个:

 try
            {
              //wcf service call
            }
            catch (FaultException ex)
            {
               throw new Exception( (ex as WebFaultException<MyContractApplicationFault>).Detail.MyContractErrorMessage );                
            }