[英]How to throw a deserialized exception?
I'm serializing a Exception at the server with JsonConvert.SerializeObject and then encoding to a byte[] and deserializing in the client using JsonConvert.DeserializeObject .我正在使用JsonConvert.SerializeObject在服务器上序列化一个异常,然后编码为byte[]并在客户端使用JsonConvert.DeserializeObject反序列化。 Everything works fine so far... The problem is when I throw the Exception the stacktrace being replaced, let me demostrate:到目前为止一切正常......问题是当我抛出异常时堆栈跟踪被替换,让我演示一下:
public void HandleException(RpcException exp)
{
// Get the exception byte[]
string exceptionString = exp.Trailer.GetBytes("exception-bin");
// Deserialize the exception
Exception exception = JsonConvert.DeserializeObject<Exception>(exceptionString, new
JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
// Log the Exception: The stacktrace is correct. Ex.: at ServerMethod()
Console.WriteLine(exception);
// Throw the same Exception: The stacktrace is changed. Ex.: at HandleException()
ExceptionDispatchInfo.Capture(exception).Throw();
}
If you deserialize an Exception
and set JsonSerializerSettings.Context = new StreamingContext(StreamingContextStates.CrossAppDomain)
then the deserialized stack trace string will get prepended to the displayed StackTrace
even after the exception is thrown:如果您反序列化Exception
并设置JsonSerializerSettings.Context = new StreamingContext(StreamingContextStates.CrossAppDomain)
则反序列化的堆栈跟踪字符串将被添加到显示的StackTrace
之前,即使在引发异常后:
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
Context = new StreamingContext(StreamingContextStates.CrossAppDomain),
};
var exception = JsonConvert.DeserializeObject<Exception>(exceptionString, settings);
Notes:笔记:
This works because, in the streaming constructor for Exception
, the deserialized stack trace string is saved into a _remoteStackTraceString
which is later prepended to the regular stack trace:这是有效的,因为在Exception
的流式构造函数中,反序列化的堆栈跟踪字符串被保存到_remoteStackTraceString
中,该字符串稍后会添加到常规堆栈跟踪中:
if (context.State == StreamingContextStates.CrossAppDomain) { //...this new exception may get thrown. It is logically a re-throw, but // physically a brand-new exception. Since the stack trace is cleared // on a new exception, the "_remoteStackTraceString" is provided to // effectively import a stack trace from a "remote" exception. So, // move the _stackTraceString into the _remoteStackTraceString. Note // that if there is an existing _remoteStackTraceString, it will be // preserved at the head of the new string, so everything works as // expected. // Even if this exception is NOT thrown, things will still work as expected // because the StackTrace property returns the concatenation of the // _remoteStackTraceString and the _stackTraceString. _remoteStackTraceString = _remoteStackTraceString + _stackTraceString; _stackTraceString = null; }
While the serialization stream for Exception
does contain the stack trace string, it does not attempt to capture the private Object _stackTrace
which is used by the runtime to identify where in the executing assembly the exception was thrown.虽然Exception
的序列化 stream 确实包含堆栈跟踪字符串,但它不会尝试捕获private Object _stackTrace
,运行时使用它来识别正在执行的程序集中的哪个位置引发了异常。 This would seem to be why ExceptionDispatchInfo
is unable to copy and use this information when throwing the exception.这似乎是ExceptionDispatchInfo
在抛出异常时无法复制和使用此信息的原因。 Thus it seems to be impossible to throw a deserialized exception and restore its "real" stack trace from the serialization stream.因此,似乎不可能抛出反序列化异常并从序列化 stream 中恢复其“真实”堆栈跟踪。
In order Json.NET to deserialize a type using its streaming constructor (and thus set the remote trace string as required), the type must be marked with [Serializable]
and implement ISerializable
.为了 Json.NET 使用其流构造函数反序列化类型(并因此根据需要设置远程跟踪字符串),该类型必须用[Serializable]
标记并实现ISerializable
。 System.Exception
meets both requirements, but some derived classes of Exception
do not always add the [Serializable]
attribute. System.Exception
满足这两个要求,但是Exception
的某些派生类并不总是添加[Serializable]
属性。 If your specific serialized exception lacks the attribute, see Deserializing custom exceptions in Newtonsoft.Json .如果您的特定序列化异常缺少该属性,请参阅反序列化 Newtonsoft.Json 中的自定义异常。
Deserializing an exception with TypeNameHandling.All
is insecure and may lead to injection of attack types when deserializing from untrusted sources.使用TypeNameHandling.All
反序列化异常是不安全的,并且在从不受信任的来源反序列化时可能导致注入攻击类型。 See: External json vulnerable because of Json.Net TypeNameHandling auto?请参阅:外部 json 易受攻击,因为 Json.Net TypeNameHandling auto? whose answer specifically discusses deserialization of exceptions.其答案专门讨论了异常的反序列化。
Just a small case that I want to point out: I'm calling this seralization/deserialization from two apps one is Blazor (.net 5) and another one is WinForms (.net framework 4.7).只是我想指出的一个小案例:我从两个应用程序中调用这种序列化/反序列化,一个是 Blazor(.net 5),另一个是 WinForms(.net framework 4.7)。 In the blazor one the method of the accepted answer did not work.在 blazor 之一中,接受答案的方法不起作用。 What I do in this case is set te RemoteStackTrace via reflection.在这种情况下,我所做的是通过反射设置远程堆栈跟踪。
// Convert string para exception
Exception exception = JsonConvert.DeserializeObject<Exception>(exceptionString, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
// Set RemoteStackTrace
exception.GetType().GetField("_remoteStackTraceString", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(exception, exception.StackTrace);
// Throw the Exception with original stacktrace
ExceptionDispatchInfo.Capture(exception).Throw();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.