Context:
What i have now:
However,
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:
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?
Is protobuf a good choice for my problem at all?
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
                ...
            };
        }
    }
}
                        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:
HttpError
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With