【问题标题】:Passing exceptions between two C# programs using JSON使用 JSON 在两个 C# 程序之间传递异常
【发布时间】:2015-07-24 11:47:07
【问题描述】:

我有一个 Web API,它向执行某些任务/命令的 Windows 服务发出 HTTP 请求。

如果我的“服务”抛出异常,我想使用 JSON 将该异常通过管道传回 Web API。然后我想将异常反序列化回异常对象并抛出它。

我的代码:

Web API 和 Service 之间的共享异常:

public class ConnectionErrorException : Exception
{
    public ConnectionErrorException()
    {
    }
    public ConnectionErrorException(String message)
        : base(message)
    {
    }
}

现在在我的服务中,我有以下代码:

       ... 
       try
        {
            result = await ExecuteCommand(userId);
            //If reached here nothing went wrong, so can return an OK result
            await p.WriteSuccessAsync();
        }
        catch (Exception e)
        {
            //Some thing went wrong. Return the error so they know what the issue is
            result = e;
            p.WriteFailure();
        }
        //Write the body of the response:

        //If the result is null there is no need to send any body, the 200 or 400 header is sufficient
        if (result != null)
        {
            var resultOutput = JsonConvert.SerializeObject(result);
            await p.OutputStream.WriteAsync(resultOutput);
        }
        ...

所以在这里我返回一个 JSON 对象。要么是实际的响应对象,要么是发生的异常。

然后是向服务发出请求的 Web API 中的代码:

  // Make request
            HttpResponseMessage response = await client.PostAsJsonAsync(((int)(command.CommandId)).ToString(), command);
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                var exception = HandleErrorResponse(await response.Content.ReadAsStringAsync());
                var type = exception.GetType();
                //TODO: try and determine which exact exception it is.
                throw exception;
            }

现在,如果响应成功,我只返回字符串内容。如果请求失败,我尝试将 json 响应传递给异常。但是我必须将它传递给基本异常,因为我还不知道它是什么类型。但是,当我在异常上调试并添加看门狗时。有一个参数_className 表示“Domain.Model.Exceptions.API.ConnectionErrorException”。

问题: 如何确定返回了哪个异常并将其反序列化回正确的异常,以便我可以再次抛出它。我需要知道确切的异常类型,因为我在 Web API 的服务层中处理所有不同的异常。

这是为ConnectionErrorException 返回的 json 示例:

{
    "ClassName": "Domain.Model.Exceptions.API.ConnectionErrorException",
    "Message": null,
    "Data": null,
    "InnerException": null,
    "HelpURL": null,
    "StackTraceString": "",
    "HResult": -2146233088,
    "Source": "LinkProvider.Logic",
    "WatsonBuckets": null
}

【问题讨论】:

    标签: c# json exception asp.net-web-api json.net


    【解决方案1】:

    用以下代码块替换您的异常处理。

    else
    {
        var response = await response.Content.ReadAsStringAsync();
        var exception = JsonConvert.DeserializeObject<Exception>(response);
        // your handling logic here
        Console.WriteLine(exception);
    }
    

    所以如果服务抛出new NotImplementedException("I haz error!"),上面会打印出System.NotImplementedException: I haz error!


    这是一个使用MVVMLightJSON.net 的快速独立示例。假设你有sender

    public class Sender
    {
        public Sender()
        {
            Messenger.Default.Register<NotificationMessage>(this, message =>
                {
                    if ((Type)message.Target == typeof(Sender))
                       GotResponse(message.Notification);
                });    
        }
    
        public void SendRequest(string request)
        {
            Console.WriteLine("sending:{0}", request);
            Messenger.Default.Send(
                new NotificationMessage(this, typeof(Receiver), request));
        }
    
        private void GotResponse(string response)
        {
            Console.WriteLine("received:{0}", response);
            if (response.Equals("ok"))
                return;
    
            Exception exception = JsonConvert.DeserializeObject<Exception>(response);
            Console.WriteLine("exception:{0}", exception);
    
            try
            {
                throw exception;
            }
            catch (Exception e)
            {
                Console.WriteLine("Indeed, it was {0}", e);
            }
        }
    }
    

    receiver

    public class Receiver
    {
        public Receiver()
        {
            Messenger.Default.Register<NotificationMessage>(this, message =>
                {
                    if ((Type)message.Target == typeof(Receiver))
                        GotRequest(message.Notification);
                }); 
        }
    
        public void SendResponse(string response)
        {
            Messenger.Default.Send(new NotificationMessage(this, typeof(Sender), response));
        }
    
        public void GotRequest(string request)
        {
            string response = !string.IsNullOrWhiteSpace(request) ? 
                              "ok" : 
                              JsonConvert.SerializeObject(new NotImplementedException("I haz error!"));
    
            SendResponse(response);
        }
    }
    

    然后跟随“激活”

    var sender = new Sender();
    var receiver = new Receiver();
    sender.SendRequest("my request");
    sender.SendRequest(null);
    

    会打印出来

    发送:我的请求
    收到:好的

    发送:
    收到:{"ClassName":"System.NotImplementedException", "Message":"...","WatsonBuckets":null}

    异常:System.NotImplementedException:我有错误!

    确实,这是 System.NotImplementedException: I haz error!在 WpfApplication1.View.Sender.GotResponse(String response) in...

    【讨论】:

    • 所以你的意思是我可以在我的对象上做一个.ToString() 并检查字符串的内容?
    • 需要改写我的(现已删除)评论。我是说您需要使用例如序列化异常。 JSON.net 然后在另一端反序列化为异常。
    【解决方案2】:

    您可以保留 C# dynamic 对象的异常,然后将其序列化为 JSON,然后从 Windows 服务返回。再次在 Web API 上反序列化该 JSON 并保存为动态对象。这样您就不必担心异常的实际类型。在任何例外情况下,您都可以将其丢弃。如果您想知道异常的实际类型,那么您可以编写这样的代码,其中tempData 是反序列化后的dynamic 对象:

    Type exceptionType = ((ObjectHandle)tempData).Unwrap().GetType();
    

    然后相应地处理异常

    希望这会有所帮助:)

    【讨论】:

    • 整个想法是我想知道抛出了什么类型的异常。在我的服务层中,我处理所有特定的错误。所以我需要得到确切的错误类型。
    • @Zapnologica 编辑了我的答案...这应该可以解决您的问题
    【解决方案3】:

    首先为了能够反序列化异常 JSON,我不得不向ConnectionErrorException 类添加一个额外的构造函数:

    public class ConnectionErrorException : Exception
    {
        // ... rest of the code
    
        protected ConnectionErrorException(SerializationInfo info, StreamingContext context) 
            : base(info, context)
        {
        }
    }
    

    这是一个已知问题。以question 为例。

    接下来我将首先读取ClassName 属性的值,然后根据该值将其反序列化为所需的类型。我认为为此创建一些辅助类是个好主意:

    public static class JsonHelper
    {
        public static bool IsInstanceOf<T>(this JsonObject jsonObject)
        {
            if (jsonObject == null || !jsonObject.ContainsKey("ClassName"))
            {
                return false;
            }
    
            return jsonObject["ClassName"] == typeof(T).FullName;
        }
    }
    

    然后您的代码可能如下所示:

    var jsonObject = JsonObject.Parse(json);
    if(jsonObject.IsInstanceOf<ConnectionErrorException>())
    {
        var connectionErrorException = 
            JsonConvert.DeserializeObject<ConnectionErrorException>(json);
    }
    

    【讨论】:

    • 关于附加构造函数的有趣评论。我的没有给我任何错误。感谢您的帮助,我会试一试,看起来是一种很好的干净方式来实现它。
    • 如果要包装实际异常,则需要添加额外的构造函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-12
    • 2011-02-08
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多