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;
}
我有另一种方法来让它工作(从客户端发送头值来修改会话行为),但是如果上面的版本工作的话会更好。
最后,我根据客户机头值设置会话行为,并根据请求生存期中稍后的操作属性验证发送该头的有效性。如果有人能解决我在上面与之战斗的动作解析代码,请随意在这里发布答案。