上下文:
我现在拥有的:
- 三层应用程序
- 客户端-服务器通信
- 服务器:ASP.NET WebApi v1
- 客户端:HttpClient
- 序列化-JSON.NET
然而,
- JSON.NET速度慢
- 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]。
问题:
我能做些什么来正确地序列化错误吗?或者我应该避免使用开箱即用的错误处理,并在使用protobuf知道的已知类型的服务器上实现我自己的错误处理吗?
对于我的问题,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"))
};
}