2017-09-27 7 views
0

Aspnet Core 1.1에서 API를 작성했지만 모든 요청은 단일 경로에 대한 게시 요청이어야하며 본문 페이로드의 유형에 따라 하나의 작업 만로드해야합니다 , 나는 ActionMethodSelectorAttribute 상속을 시도하고 IsValidForRequest을 구현 한 다음이 간단한 접근 방식으로 예상되는 형식을 전달하는 동작을 구현하고 작동하지만 문제가 발생하면 RouteContext.HttpContext.Request.Body이 스트림 개체이고 더 deserialize하려고하면 발생합니다 한번은 예외를 throw하고 예외를 피하는 대신 캐시를 사용하여 예외를 피하는 데 도움이되었습니다. 그러나 Action이 선택되면 곧 본문이 소비되고 다시 모델을 직렬화하고 모델 바인더에 사용할 수 없습니다.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); 
} 

이 방법은 규모가 크지 않으며 다른 요구 사항을 충족하는 솔루션이나 의견을 듣게되어 기쁩니다.

답변

0

마지막으로 나는이 작업을 얻을, 키 포인트는 재사용을 넣어하여 몸 스트림을 허용 routeContext.HttpContext.Request.EnableRewind()에 메소드 호출 자사의 position = 0

내가 오버 헤드를 완화하기 위해 메모리 캐시를 사용했다. 여기

내 솔루션

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); 
     }