我有错误处理程序,在FaultException中转换Exception并在客户端发送它。 代码:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
var exceptionDetail = new ExceptionDetail(error);
var faultException = new FaultException<ExceptionDetail>(exceptionDetail, error.Message);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
}
但在这种情况下,服务在没有InnerException的情况下获得Exception,而InnerException可能包含另一个InnerException。 是否可以将服务器上的异常传递给客户端而不转换为FaultException,或者确保使用所有InnerExceptions的FaultException进行传输。
答案 0 :(得分:1)
我想你想要将在WebService /“server”上发生的.NET Exception
打包/序列化为FaultException
,然后当“客户端”收到它时,它会被解压缩并且提升为原始常规.NET Exception
....而不仅仅是FaultException
- 这称为“异常编组”。
当您无法修改客户端代码以添加FaultException
处理,或者您不希望客户端代码被“webservice”特定知识污染时,有时需要这样做,即您的代码只是处理.NET Exceptions。
(注意:这是绕过“异常屏蔽”......这有点令人不悦,因为您可能会在传输到客户端的异常数据中公开可能允许攻击向量的细节......此外它可能会使您的“webservice” “不太可互操作......即非.NET客户端需要知道如何理解序列化的.NET异常信息。”
因此,在“服务器”上,您有正确的想法,即在您的服务中添加一个实现IErrorHandler
的行为。
但你应该尝试“序列化”Exception
,但如果无法完成,请回到Exception.Message
。
有ProvideFault
的一些不同实现可以做到这一点......选择你最喜欢的那个。
“ppwcode”是最好的,因为它重建堆栈跟踪,并递归链以记录InnerExceptions
。这不完全正确,因为它只检查“root”异常的Serializable属性....所以如果任何InnerExceptions
不可序列化,那么它将无效。
这是我最终用于在服务器上打包Exception的内容:
void IErrorHandler.ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (error is FaultException)
{
// Let WCF do normal processing
}
else
{
try
{
// This takes an exception, and tries to serialize it, so
// that it can be encoded into a <soap:Fault> <soap:Detail>
// element. The client, can then deserialize the detail to
// rebuild the exception information.
//
// Not all exceptions are serializable! Thus if our
// serialization fails, then we will instead return a
// general exception whose content will be string
// representatiion of the exception.
MessageFault messageFault = MessageFault.CreateFault(
new FaultCode("ExceptionMarshallingErrorHandlerV1"),
new FaultReason(error.Message),
error,
new NetDataContractSerializer());
if (messageFault != null)
{
fault = Message.CreateMessage(version, messageFault, null);
}
}
catch (Exception)
{
try
{
// This isn't strictly correct, as the "stack trace" information
// is lost, and the InnerExceptions too....but I didn't think them
// important at the time...and anyway all the exceptions I expected
// were serializable, so this shouldn't have been hit.
//
// This is effectively a different way to create the MessageFault
// compared to yours.
//
// If you look at the "ppwcode" code, then it tries to
// handle the non-serializable exceptions in a better way...by
// recording the stack trace and InnerExceptions.
//
// So I would suggest using:
//
// Exception ex = HandleNonSerializableException(error);
Exception ex = new Exception(error.ToString());
MessageFault messageFault = MessageFault.CreateFault(
new FaultCode("ExceptionMarshallingErrorHandlerV1"),
new FaultReason(error.Message),
ex,
new NetDataContractSerializer());
if (messageFault != null)
{
fault = Message.CreateMessage(version, messageFault, null);
}
}
catch
{
// Pass the "exception" back as a FaultException
MessageFault messageFault = MessageFault.CreateFault(
new FaultCode("Exception (non-serializable)"),
new FaultReason(error.Message));
if (messageFault != null)
{
fault = Message.CreateMessage(version, messageFault, null);
}
fault = Message.CreateMessage(version, messageFault, null);
}
}
}
}
来自“ppwcode”:
private static Exception HandleNonSerializableException(Exception e)
{
string msg = e.Message;
msg = string.Format(
"Exception of type {1} wasn't serializable, rethrown as plain exception.{0}{0}Original message:{0}{2}{0}{0}Original Stacktrace:{0}{3}",
Environment.NewLine,
e.GetType().Name,
msg,
e.StackTrace);
Exception inner = e.InnerException;
while (inner != null)
{
msg = string.Format(
"{1}{0}{0}InnerException:{0}{2}{0}{0}StackTrace:{0}{3}",
Environment.NewLine,
msg,
inner.Message,
inner.StackTrace);
inner = inner.InnerException;
}
return new ProgrammingError(msg);
}
现在在客户端上,您需要访问返回给它的FaultException
并解压缩其中包含的异常并将其作为.NET Exception
提升。
所以你需要一些“挂钩”到客户端通道堆栈的东西......并“检查”收到的消息回复 - IClientMessageInspector
有AfterReceiveReply
....这会给你那个机会。
有几种不同的方法可以将“检查器”应用于客户端通道堆栈。
一种方法是创建自定义服务合同的自定义Attribute
。此Attribute
实施IContractBehaviour
,其中ApplyClientBehavior
为您提供了指定自定义消息检查器的机会。
(您也可以使用此自定义Attribute
作为通过IErrorHandler
)
ApplyDispatchBehavior
的方法
“olegsych”链接有一个合适的“消息检查器”来解压异常和一个名为ExceptionMarshallingBehavior
的自定义属性,您可以使用它来应用它(它还有一个IErrorHandler
可以执行也包装你....)只需将它应用于服务合同。
所以:
[ServiceContract(
......
)
]
[ExceptionMarshallingBehavior]
[ServiceKnownType(typeof(.....))]
[ServiceKnownType(typeof(.....))]
public interface IMyWebService
{
[OperationContract]
string GetVersion();
...
}