问题描述
我正在使用 JsonConvert.SerializeObject 在服务器上序列化 Exception ,然后编码为 byte [] 并在客户端中反序列化使用 JsonConvert.DeserializeObject .到目前为止一切正常...问题是当我抛出 Exception 被替换的堆栈跟踪时,让我演示一下:
public void HandleException(RpcException exp){//获取异常字节[]字符串exceptionString = exp.Trailer.GetBytes("exception-bin");//反序列化异常异常exception = JsonConvert.DeserializeObject< Exception>(exceptionString,newJsonSerializerSettings {TypeNameHandling = TypeNameHandling.All});//记录异常:stacktrace是正确的.例如:在ServerMethod()Console.WriteLine(exception);//抛出相同的异常:更改了堆栈跟踪.例如:在HandleException()ExceptionDispatchInfo.Capture(exception).Throw();}
如果反序列化 Exception
并设置 JsonSerializerSettings.Context = new StreamingContext(StreamingContextStates.CrossAppDomain)
,然后反序列化的堆栈跟踪字符串将被添加到显示的="https://docs.microsoft.com/zh-cn/dotnet/api/system.exception.stacktrace?view=net-5.0" rel ="nofollow noreferrer"> StackTrace
即使在引发异常之后:
var设置=新的JsonSerializerSettings{TypeNameHandling = TypeNameHandling.All,上下文=新的StreamingContext(StreamingContextStates.CrossAppDomain),};var exception = JsonConvert.DeserializeObject< Exception>(exceptionString,设置);
注意:
-
之所以可行,是因为在用于
Exception
,反序列化的堆栈跟踪字符串将保存到_remoteStackTraceString
中,该前缀随后会添加到常规堆栈跟踪中: -
虽然
Exception
的序列化流确实包含堆栈跟踪字符串,但它不会尝试捕获私有对象_stackTrace
,运行时使用它来识别在执行程序集中的哪个位置抛出了异常.这似乎就是为什么ExceptionDispatchInfo
> 在引发异常时无法复制和使用此信息.因此,似乎不可能抛出反序列化的异常并恢复其真实"异常.序列化流中的堆栈跟踪. -
为了使Json.NET使用其流构造函数反序列化类型(从而根据需要设置远程跟踪字符串),必须使用
[Serializable]
并实现ISerializable
.System.Exception
满足这两个要求,但是某些Exception
的派生类并不总是添加[Serializable]
属性.如果您的特定序列化异常缺少该属性,请参见 在Newtonsoft.Json 中反序列化自定义异常./p> -
使用
TypeNameHandling.All
反序列化异常是不安全的,并且在从不受信任的来源反序列化时可能导致注入攻击类型.请参阅: 由于Json.Net TypeNameHandling auto,外部JSON容易受到攻击? 其答案专门讨论了反序列化例外.
演示小提琴此处.
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. 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:
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:While the serialization stream for
Exception
does contain the stack trace string, it does not attempt to capture theprivate Object _stackTrace
which is used by the runtime to identify where in the executing assembly the exception was thrown. This would seem to be whyExceptionDispatchInfo
is unable to copy and use this information when throwing the exception. Thus it seems to be impossible to throw a deserialized exception and restore its "real" stack trace from the serialization 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 implementISerializable
.System.Exception
meets both requirements, but some derived classes ofException
do not always add the[Serializable]
attribute. If your specific serialized exception lacks the attribute, see Deserializing custom exceptions in Newtonsoft.Json.Deserializing an exception with
TypeNameHandling.All
is insecure and may lead to injection of attack types when deserializing from untrusted sources. See: External json vulnerable because of Json.Net TypeNameHandling auto? whose answer specifically discusses deserialization of exceptions.
Demo fiddle here.
这篇关于如何引发反序列化异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!