2017-09-27 4 views
0

私はAspnet Core 1.1でAPIを書いていましたが、すべてのリクエストは単一ルートへのポストリクエストでなければならず、ボディペイロードのタイプに応じて、私はActionMethodSelectorAttributeを継承し、IsValidForRequestを継承して、単純なアプローチであると予想されるタイプを渡すアクションを装飾していますが、動作させると問題が発生します。RouteContext.HttpContext.Request.BodyこれはStreamオブジェクトであり、さらにデシリアライズしようとする例外を避けるためにキャッシュを使用しましたが、アクションが選択されるとすぐに、本体はすでに消費されており、再度シリアライズしてモデルバインダーに使用することはできません。Aspnetコアの複雑なパラメータでアクションを選択

public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action) 
{ 

    try 
    { 
     var body = new MemoryStream(); 
     routeContext.HttpContext.Request.Body.CopyTo(body); 

     body.Position = 0; 

     XmlDocument xmlDoc = new XmlDocument(); 
     var xml = XDocument.Load(body); 
     var messageName = xml.Root.Name.LocalName; 
     return messageName == _messageType.Name; 

    } 
    catch (Exception ex) 
    { 
     return false; 
    } 

} 

[MessagBasedControllerActionSelector(typeof(OTA_HotelInvCountRQ))] 
public async Task<IActionResult> OTA_HotelInvCount([FromBody]OTA_HotelInvCountRQ request) 
{ 
    var response = await _otaService.OTA_HotelInvCountRQ(request, GetExternalProviderId()); 
    return Ok(response); 
} 

私はこのアプローチが規模に関係なく、私の要求を満たす別の解決策または1つを聞いてうれしいことを知っています。

答えて

0

最後に、私はこの作業を取得、重要な点は、それは私がオーバーヘッドを軽減するためにメモリキャッシュを使用してposition = 0

を置くことにより、再利用身体ストリーム許可routeContext.HttpContext.Request.EnableRewind()へのメソッド呼び出しです。ここで

私の解決策

public class MessagBasedControllerActionSelectorAttribute : ActionMethodSelectorAttribute 
    { 
     private const string RequestHashCodeKey = "RequestHashCodeKey"; 
     private const string MessageRootNameKey = "MessageRootNameKey"; 
     private readonly string _messageTypeName; 
     private ICacheService _cache; 

     public MessagBasedControllerActionSelectorAttribute(string messageTypeName) 
     { 
      _messageTypeName = messageTypeName; 
     } 

     public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action) 
     { 
      //Get reference to cache from DI container 
      _cache = routeContext.HttpContext.RequestServices.GetService(typeof(ICacheService)) as ICacheService; 

      //Get Request hashCode from cache if is possible 
      var existingRequestHash = _cache.Get<string>(RequestHashCodeKey); 
      var req = routeContext.HttpContext.Request; 
      var messageName = string.Empty; 
      int.TryParse(existingRequestHash, out int cacheRequestHash); 

      //Verify if the incoming request is the same that has been cached before or deserialize the body in case new request 
      if (cacheRequestHash != req.GetHashCode()) 
      { 
       //store new request hash code 
       _cache.Store(RequestHashCodeKey, req.GetHashCode().ToString()); 
       //enable to rewind the body stream this allows then put position 0 and let the model binder serialize again 
       req.EnableRewind(); 
       var xmlBody = ""; 
       req.Body.Position = 0; 

       //Read XML 
       using (StreamReader reader 
        = new StreamReader(req.Body, Encoding.UTF8, true, 1024, true)) 
       { 
        xmlBody = reader.ReadToEnd(); 
       } 


       XmlDocument xmlDoc = new XmlDocument(); 
       var xml = XDocument.Parse(xmlBody); 
       //Get the root name 
       messageName = xml.Root.Name.LocalName; 
       //Store the root name in cache 
       _cache.Store(MessageRootNameKey, messageName); 

       req.Body.Position = 0; 
      } 
      else 
      { 
       //Get root name from cache 
       messageName = _cache.Get<string>(MessageRootNameKey); 
      } 

      return messageName == _messageTypeName; 
     } 
    } 

とコントローラでこのようにそれを使用します。

[MessagBasedControllerActionSelector(nameof(My_Request_Type))] 
     public async Task<IActionResult> MyAction([FromBody]My_Request_Typerequest) 
     { 
      //Some cool stuff 
      return Ok(response); 
     } 
関連する問題