3

語境:ASP.NET的WebAPI與Protocol Buffers的 - 錯誤處理

我現在擁有的一切:

  1. 3層應用
  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的一個實例(其從詞典[字符串對象]繼承),並將其寫入到該內容。

默認情況下,protobuf網對此一無所知HttpError所以我嘗試添加HttpError到protobuf的運行模式如下

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

,但它並沒有幫助,當我打電話

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

它拋出InvalidOperationException:沒有爲類型System.Object定義的序列化程序。 可能由於protobuf-net不支持的Dictionary [string,object]類型。

問題:

  1. 有什麼我能做的正確序列化的錯誤,或者我應該避免使用外的開箱errorhanding和實現自己的錯誤處理,使用以及在服務器上 - protobuf知道的知識類型?

  2. protobuf是我的問題的不錯選擇嗎?

回答

2

我已經通過向我的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")) 
     }; 
    } 
2

你最好的選擇是使用代理;類似:

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

哪裏MyError是你的一個自定義的類型,其中:

  • 具有從HttpError
  • 轉換運算符(隱式或顯式)到/適於從protobuf網使用
+0

感謝您的回答,使System.Web.Http.HttpError的系列化。這雖然沒有幫助。我按照你所說的做了,但typeModel.Compile()拋出ArgumentException:重複的數據(列表,集合等)具有內置行爲,不能使用代理。 – olldman

+0

@olldman是HttpError的一個列表? –

+0

它是一個字典[字符串,對象] – olldman

3

這裏是一個片段使用protobuf網

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 
       ... 
      }; 
     } 
    } 
} 
+0

非常有用的解決方案。 – rolls