Я хотел, чтобы приложение WebAPI изменило используемые SessionStateBehavior
на основе таких атрибутов действий:
[HttpPost]
[Route("api/test")]
[SetSessionStateBehavior(SessionStateBehavior.Required)] // <--- This modifies the behavior
public async Task<int> Test(){}
Кажется, однако, что единственное место, где я могу изменить поведение сеанса, находится внутри Application_PostAuthorizeRequest
моего HttpApplication
(или в аналогичных местах, в начале срока действия запроса), в противном случае я получаю эту ошибку:
'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
из запроса.
Есть идеи, как это получить?
Это не внутри контроллера, поэтому я не готов там.
Или есть лучший способ сделать это?
Я пытаюсь увидеть дизассемблированный код фреймворка, чтобы скопировать его части, но в этот момент я совершенно растерялся ...
UPDATE:
Это самое близкое к разрешению действие вручную, но оно не работает:
Я зарегистрировал эти две службы:
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;
}
У меня есть альтернативный хак, чтобы заставить его работать (отправка значений заголовка от клиента для изменения поведения сеанса), но было бы неплохо, если бы вышеприведенная версия работала.
UPDATE:
В конце концов, я приступил к настройке поведения сеанса на основе значения заголовка клиента и проверке правильности отправки этого заголовка на основе атрибутов действия позже во время существования запроса. Если кто-то может решить код разрешения действия, с которым я боролся, смело отправляйте ответ здесь.