Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET WebApi with Protocol Buffers - Error handling

Context:

What i have now:

  1. 3-tiers app
  2. Client-Server communication
    • Server: ASP.NET WebApi v1
    • Client: HttpClient
  3. Serialization - JSON.NET

However,

  1. JSON.NET is slow
  2. JSON.NET is even slower on the first call (i take it this is because of serializer assembly generation on the fly). This is too slow for me - according the requirements i need to optimize the first call as much as possible.

I am considering using protobuf-net instead of JSON.NET. On a simple PoC app it has shown more than twice as fast result, even the first call, especially when i'd pre-generated the assembly for protocol buffers serializer.

So i've implemented MediaTypeFormatter using protobuf-net and everything works well except one thing - serializing errors.

This is how the exceptions are passed to the client:

public class ExceptionShielderAttribute : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        context.Response = context.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, context.Exception);
    }
}

Internally, CreateErrorResponse method creates an instance of HttpError (which inherits from Dictionary[string, object]) and writes it to the content.

By default, protobuf-net knows nothing about HttpError so I've tried to add HttpError to protobuf runtime model as follows

typeModel.Add(typeof (HttpError), true);

but it did not help, when i call

typeModel.Compile("ModelSerializer", "ModelSerializer.dll")

it throws InvalidOperationException: No serializer defined for type: System.Object. Likely due to type of Dictionary[string, object] which is not supported by protobuf-net.

Questions:

  1. Is there anything I can do to serialize errors properly or should i avoid using out-of-the-box errorhanding and implement my own error handling on the server that uses well-know types that protobuf is aware of?

  2. Is protobuf a good choice for my problem at all?

like image 753
olldman Avatar asked Feb 13 '23 19:02

olldman


2 Answers

Here is a snippet that enable serialization of System.Web.Http.HttpError using protobuf-net

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
                ...
            };
        }
    }
}
like image 117
c-romeo Avatar answered Feb 27 '23 13:02

c-romeo


Your best bet would be to use a surrogate; something like:

typeModel.Add(typeof(HttpError), false)
    .SetSurrogate(typeof(MyError));

Where MyError is a custom type of yours which:

  • has a conversion operator (implicit or explicit) to/from HttpError
  • is suitable for use from protobuf-net
like image 33
Marc Gravell Avatar answered Feb 27 '23 12:02

Marc Gravell