【问题标题】:Aspnet Core Select action by complex parameter in the bodyAspnet Core 通过正文中的复杂参数选择操作
【发布时间】:2018-02-02 00:35:57
【问题描述】:

我在 Aspnet Core 1.1 中编写了一个 API,但其中一个要求是每个请求都必须是对单个路由的发布请求,并且根据主体有效负载中的类型加载一个动作,我尝试继承 ActionMethodSelectorAttribute 并实现 @ 987654322@ 然后装饰每个传递预期类型的​​ Action 这是一种简单的方法,它可以工作,但是当我尝试使用 RouteContext.HttpContext.Request.Body 它是一个 Stream 对象时出现问题,如果我尝试多次反序列化它会抛出和异常,我还使用了缓存,这帮助我避免了异常,但是一旦选择了 Action,body 就已经消耗掉了,无法再次用于序列化它并用于模型绑定。

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

我知道这种方法无法扩展,我很乐意听取另一种解决方案或满足我要求的解决方案。

【问题讨论】:

    标签: c# asp.net-core asp.net-core-webapi


    【解决方案1】:

    最后我得到了这个工作,关键是对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);
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-01
      • 1970-01-01
      • 2015-10-30
      • 2016-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多