c# - WebAPI - 从请求中手动解析控制器和操作
问题描述
我想让我的 WebAPI 应用程序更改SessionStateBehavior
基于操作属性的使用,如下所示:
[HttpPost]
[Route("api/test")]
[SetSessionStateBehavior(SessionStateBehavior.Required)] // <--- This modifies the behavior
public async Task<int> Test(){}
但是,似乎我可以更改会话行为的唯一位置是在 my 内部(或在请求生命周期早期的类似位置),否则我会收到此错误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;
}
我有另一种方法可以使它工作(从客户端发送标头值以修改会话行为),但是如果上面的版本可以工作,那就太好了。
更新:
最终,我根据客户端标头值设置会话行为,并在请求生命周期的后期根据操作属性验证发送该标头的有效性。如果有人可以解决我在上面遇到的动作解析代码,请随时在此处发布答案。
解决方案
我不知道这是否会对您有所帮助,但我只是在学习 Pluralsight 课程(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));
推荐阅读
- c# - 选择两个文件时,从右键单击 Windows 上下文菜单中隐藏一个选项
- django - 选择 ForeignKey 的最新条目并使用 Django ORM 作为嵌套对象返回
- java - 在运行时进程中执行“启动”
- sql-server - 存储过程中的 IF ELSE 条件以包含或排除周末数据
- postgresql - Escape \n when exporting CSV using COPY command
- r - 在 R 中跨多个列标记不同的组合
- angular - 单击按钮时从传单地图中删除多边形
- javascript - 在javascript中链接多个异步函数
- android - Google Play 游戏服务,SHA1 公版要添加哪个密钥?
- sql - 分区表 SQL 的别名