Asp.net web api 带有协议缓冲区的ASP.NET WebApi-错误处理

Asp.net web api 带有协议缓冲区的ASP.NET WebApi-错误处理,asp.net-web-api,error-handling,json.net,protocol-buffers,protobuf-net,Asp.net Web Api,Error Handling,Json.net,Protocol Buffers,Protobuf Net,上下文: 我现在所拥有的: 三层应用程序 客户机-服务器通信 服务器:ASP.NET WebApi v1 客户端:HttpClient 序列化-JSON.NET 但是, JSON.NET速度很慢 在第一次调用时,JSON.NET甚至更慢(我认为这是因为序列化程序集的动态生成)。这对我来说太慢了——根据需要,我需要尽可能优化第一次调用 我正在考虑使用JSON.NET而不是JSON.NET。在一个简单的PoC应用程序上,即使是第一次调用,它也显示了两倍多的快速结果,特别是当我为协议缓冲区序列化程

上下文:

我现在所拥有的:

  • 三层应用程序
  • 客户机-服务器通信
    • 服务器:ASP.NET WebApi v1
    • 客户端:HttpClient
  • 序列化-JSON.NET
  • 但是,

  • JSON.NET速度很慢
  • 在第一次调用时,JSON.NET甚至更慢(我认为这是因为序列化程序集的动态生成)。这对我来说太慢了——根据需要,我需要尽可能优化第一次调用
  • 我正在考虑使用JSON.NET而不是JSON.NET。在一个简单的PoC应用程序上,即使是第一次调用,它也显示了两倍多的快速结果,特别是当我为协议缓冲区序列化程序预生成程序集时

    因此,我已经使用protobuf net实现了MediaTypeFormatter,除了一件事——序列化错误,其他一切都很好

    以下是异常传递给客户端的方式:

    public class ExceptionShielderAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            context.Response = context.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, context.Exception);
        }
    }
    
    在内部,CreateErrorResponse方法创建HttpError的实例(从Dictionary[string,object]继承)并将其写入内容

    默认情况下,protobuf net对HttpError一无所知,所以我尝试将HttpError添加到protobuf运行时模型中,如下所示

    typeModel.Add(typeof (HttpError), true);
    
    但是,当我打电话时,这没有帮助

    typeModel.Compile("ModelSerializer", "ModelSerializer.dll")
    
    它抛出InvalidOperationException:没有为类型System.Object定义序列化程序。 可能是由于protobuf net不支持的字典类型[string,object]

    问题:

  • 我可以做些什么来正确地序列化错误,还是应该避免使用现成的错误处理,并在使用protobuf知道的众所周知的类型的服务器上实现自己的错误处理

  • protobuf是解决我问题的好选择吗


  • 你最好的选择是使用代理;比如:

    typeModel.Add(typeof(HttpError), false)
        .SetSurrogate(typeof(MyError));
    
    其中
    MyError
    是您的自定义类型,它:

    • 有一个转换运算符(隐式或显式)到/从
      HttpError
    • 适用于protobuf网络

    我通过在protobuf媒体类型格式化程序中添加一个特殊情况来解决此问题:

        public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
        {
            var completionSource = new TaskCompletionSource<object>();
            try
            {
                if (type == typeof(HttpError))
                {
                    value = CreateDto((HttpError) value);
                }
                model.Value.Serialize(stream, value);
                completionSource.SetResult((object)null);
            }
            catch (Exception ex)
            {
                completionSource.SetException(ex);
            }
            return (Task)completionSource.Task;
        }
    
        private HttpErrorDto CreateDto(HttpError error)
        {
            if (error == null) return null;
    
            return new HttpErrorDto
            {
                Message = error.GetValueOrDefault<string>("Message"),
                ExceptionMessage = error.GetValueOrDefault<string>("ExceptionMessage"),
                StackTrace = error.GetValueOrDefault<string>("StackTrace"),
                ExceptionType = error.GetValueOrDefault<string>("ExceptionType"),
                InnerException = CreateDto(error.GetValueOrDefault<HttpError>("InnerException"))
            };
        }
    
    public override Task WriteToStreamAsync(类型、对象值、流、HttpContent内容、TransportContext)
    {
    var completionSource=new TaskCompletionSource();
    尝试
    {
    if(type==typeof(HttpError))
    {
    值=CreateDto((HttpError)值);
    }
    model.Value.Serialize(流,值);
    completionSource.SetResult((对象)null);
    }
    捕获(例外情况除外)
    {
    completionSource.SetException(ex);
    }
    返回(任务)completionSource.Task;
    }
    私有HttpErrordToCreatedTo(HttpError错误)
    {
    if(error==null)返回null;
    返回新的HttpErrorDto
    {
    Message=错误。GetValueOrDefault(“消息”),
    ExceptionMessage=error.GetValueOrDefault(“ExceptionMessage”),
    StackTrace=error.GetValueOrDefault(“StackTrace”),
    ExceptionType=错误。GetValueOrDefault(“ExceptionType”),
    InnerException=CreateDto(error.GetValueOrDefault(“InnerException”))
    };
    }
    
    下面是一个使用protobuf net实现System.Web.Http.HttpError序列化的代码片段

    namespace WS
        using System;
        using System.Runtime.Serialization;
        using System.Web.Http;
        using WebApiContrib.Formatting;
    
        public static class WebApiConfig
        {
            public static void Register(HttpConfiguration config)
            {
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                );
    
                config.Formatters.Add(new ProtoBufFormatter());
    
                ProtoBufFormatter.Model.Add(typeof(HttpErrorProto), true);
                var model = ProtoBufFormatter.Model.Add(typeof(HttpError), false);
                model.IgnoreListHandling = true;
                model.SetSurrogate(typeof(HttpErrorProto));
            }
        }
    
        [DataContract]
        public class HttpErrorProto
        {
            [DataMember(Order = 1)]
            public String ExceptionMessage { get; set; }
            [DataMember(Order = 2)]
            public String ExceptionType { get; set; }
            [DataMember(Order = 3)]
            public String InnerException { get; set; }
            [DataMember(Order = 4)]
            public String MessageDetail { get; set; }
            [DataMember(Order = 5)]
            public String Message { get; set; }
            [DataMember(Order = 6)]
            public String ModelState { get; set; }
            [DataMember(Order = 7)]
            public String StackTrace { get; set; }
    
    
            public static implicit operator HttpErrorProto(HttpError error)
            {
                return error == null ? null : new HttpErrorProto
                {
                    ExceptionMessage = error.ContainsKey("ExceptionMessage") ? error["ExceptionMessage"] as string : null,
                    ExceptionType = error.ContainsKey("ExceptionType") ? error["ExceptionType"] as string : null,
                    InnerException = error.ContainsKey("InnerException") ? error["InnerException"] as string : null,
                    MessageDetail = error.ContainsKey("MessageDetail") ? error["MessageDetail"] as string : null,
                    Message = error.Message,
                    ModelState = error.ContainsKey("ModelState") ? error["ModelState"] as string : null,
                    StackTrace = error.ContainsKey("StackTrace") ? error["StackTrace"] as string : null
                };
            }
    
            public static implicit operator HttpError(HttpErrorProto error)
            {
                return error == null ? null : new HttpError
                {
                    Message = error.Message
                    ...
                };
            }
        }
    }
    

    谢谢你的回答。但这并没有帮助。我按照你说的做了,但是typeModel.Compile()抛出了ArgumentException:重复数据(列表、集合等)具有内置行为,不能使用代理项。它是字典[字符串、对象]@olldman它是字典吗?还是有字典?但从根本上说:这个对象是行不通的。例如,如果您可以在从模型到DTO的转换过程中串值,例如它是一个字典-请在这里找到它的源代码,至于您的建议,我认为当调用CreateErrorResponse(此处-)时,不可能像WebApi故意这样做。看起来自定义错误处理是唯一的方法:(@olldman)这很尴尬。我可能可以编写代码来解决它,但它在今天不起作用。这是一个非常有用的解决方案。