简体   繁体   English

如何抛出反序列化的异常?

[英]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.其答案专门讨论了异常的反序列化。

Demo fiddle here .演示小提琴在这里

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM