我刚开始使用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应该是完全可编译的并且可以运行。
答案 0 :(得分:3)
无法在WCF REST客户端(从WCF 4.0版开始)服务中使用Faults / FaultContract作为错误处理机制:
WCF列表中的每one thread:
在WCF Rest服务中没有SOAP消息,因此您无法将FaultException返回给客户端。实际上,相应的状态代码作为HTTP标头返回给请求者,允许请求者确定调用的结果。
错误是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 );
}