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



上下文:

我现在拥有的:

  1. 三层应用程序
  2. 客户端-服务器通信
    • 服务器:ASP.NET WebApi v1
    • 客户端:HttpClient
  3. 序列化-JSON.NET

然而,

  1. JSON.NET速度慢
  2. JSON.NET在第一次调用时甚至更慢(我认为这是因为串行程序集正在动态生成(。这对我来说太慢了——根据要求,我需要尽可能优化第一次通话

我正在考虑使用protobuf-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不支持Dictionary类型[string,object]。

问题:

  1. 我能做些什么来正确地序列化错误吗?或者我应该避免使用开箱即用的错误处理,并在使用protobuf知道的已知类型的服务器上实现我自己的错误处理吗?

  2. 对于我的问题,protobuf是一个好的选择吗?

以下是使用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.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"))
        };
    }

相关内容

  • 没有找到相关文章

最新更新