代码之家  ›  专栏  ›  技术社区  ›  user2173353

WebAPI-从请求中手动解析控制器和操作

  •  1
  • user2173353  · 技术社区  · 6 年前

    SessionStateBehavior 基于这样的动作属性:

        [HttpPost]
        [Route("api/test")]
        [SetSessionStateBehavior(SessionStateBehavior.Required)]    // <--- This modifies the behavior
        public async Task<int> Test(){}
    

    然而,似乎唯一能改变会话行为的地方是 HttpApplication Application_PostAuthorizeRequest (或者在类似的地方,请求生存期的早期),否则我会得到以下错误:

    'HttpContext.SetSessionStateBehavior' can only be invoked before 'HttpApplication.AcquireRequestState' event is raised.
    

    所以,在这一点上没有控制器或动作解析,所以我不知道将调用什么动作来检查其属性。 所以,我想手动解决这个问题。

    我先从这几行代码开始解析控制器:

     var httpCtx = HttpContext.Current;
     var ctrlSel = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IHttpControllerSelector)) as IHttpControllerSelector;
     var actionSel = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IHttpActionSelector)) as IHttpActionSelector;
     HttpControllerDescriptor controllerDescriptor = ctrlSel.SelectController(httpCtx.Request);
    

    但在最后一行我找不到合适的 HttpRequestMessage 从请求中。 知道怎么弄的吗? 这不在控制器里,所以我没有准备好。 我正试图看到框架的反汇编代码来复制它的一部分,但是我在这一点上迷失了方向。。。


    更新:

    这是我最接近手动解析操作的一次,但它不起作用:

    我已经注册了这两项服务:

                container.RegisterType<IHttpControllerSelector, DefaultHttpControllerSelector>();
                container.RegisterType<IHttpActionSelector, ApiControllerActionSelector>();
    

    …并尝试获取所需的会话行为,如下所示:

        private  SessionStateBehavior GetDesiredSessionBehavior(HttpContext httpCtx)
        {
            var config = GlobalConfiguration.Configuration;
            var diResolver = config.Services;
            var ctrlSel = diResolver.GetService(typeof(IHttpControllerSelector)) as IHttpControllerSelector;
             var actionSel = diResolver.GetService(typeof(IHttpActionSelector)) as IHttpActionSelector;
    
            if (ctrlSel is null || actionSel is null)
            {
                return DefaultSessionBehavior;
            }
    
    
            var method = new HttpMethod(httpCtx.Request.HttpMethod);
            var requestMsg = new HttpRequestMessage(method, httpCtx.Request.Url);
            requestMsg.Properties.Add(HttpPropertyKeys.RequestContextKey, httpCtx.Request.RequestContext);
            requestMsg.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, config);
            httpCtx.Request.Headers.Cast<string>().ForEach(x => requestMsg.Headers.Add(x, httpCtx.Request.Headers[x]));
    
    
            var httpRouteData = httpCtx.Request.RequestContext.RouteData;
            var routeData = config.Routes.GetRouteData(requestMsg);
            requestMsg.Properties.Add(HttpPropertyKeys.HttpRouteDataKey, routeData);
            requestMsg.SetRequestContext(new HttpRequestContext(){RouteData = routeData });
            requestMsg.SetConfiguration(config);
            var route = config.Routes["DefaultApi"];
            requestMsg.SetRouteData(routeData ?? route.GetRouteData(config.VirtualPathRoot, requestMsg));
    
            var routeHandler = httpRouteData.RouteHandler ?? new WebApiConfig.SessionStateRouteHandler();
            var httpHandler = routeHandler.GetHttpHandler(httpCtx.Request.RequestContext);
            if (httpHandler is IHttpAsyncHandler httpAsyncHandler)
            {
                httpAsyncHandler.BeginProcessRequest(httpCtx, ar => httpAsyncHandler.EndProcessRequest(ar), null);
            }
            else
            {
                httpHandler.ProcessRequest(httpCtx);
            }
    
            var values = requestMsg.GetRouteData().Values; // Hm this is empty and makes the next call fail...
            HttpControllerDescriptor controllerDescriptor = ctrlSel.SelectController(requestMsg);
    
            IHttpController controller = controllerDescriptor?.CreateController(requestMsg);
            if (controller == null)
            {
                return DefaultSessionBehavior;
            }
    
            var ctrlContext = CreateControllerContext(requestMsg, controllerDescriptor, controller);
            var actionCtx = actionSel.SelectAction(ctrlContext);
            var attr = actionCtx.GetCustomAttributes<ActionSessionStateAttribute>().FirstOrDefault();
            return attr?.Behavior ?? DefaultSessionBehavior;
        }
    

    我有另一种方法来让它工作(从客户端发送头值来修改会话行为),但是如果上面的版本工作的话会更好。

    最后,我根据客户机头值设置会话行为,并根据请求生存期中稍后的操作属性验证发送该头的有效性。如果有人能解决我在上面与之战斗的动作解析代码,请随意在这里发布答案。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Dan Dumitru    6 年前

    我不知道这对你有没有帮助,但我只是在学一门多元视觉课程( https://app.pluralsight.com/player?course=implementing-restful-aspdotnet-web-api )在版本控制一章中,作者展示了如何在 有权访问请求。

    public class CountingKsControllerSelector : DefaultHttpControllerSelector
    {
      private HttpConfiguration _config;
      public CountingKsControllerSelector(HttpConfiguration config)
        : base(config)
      {
        _config = config;
      }
    
      public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
      {
        var controllers = GetControllerMapping();
    
        var routeData = request.GetRouteData();
    
        var controllerName = (string)routeData.Values["controller"];
    
        HttpControllerDescriptor descriptor;
    
        if (controllers.TryGetValue(controllerName, out descriptor))
        {
          [...]
    
          return descriptor;
        }
    
        return null;
      }
    }
    

    注册地是 WebApiConfig 使用:

    config.Services.Replace(typeof(IHttpControllerSelector),
      new CountingKsControllerSelector(config));