在ASP.NET 5中,如何获取中间件中选择的路由?
我正在构建一个将托管动态页面,静态内容和REST Web API的ASP.NET 5(vNext)站点。 我已经找到了如何使用新的ASP.NET方式创建中间件的例子,但我遇到了一些障碍。
我正在尝试编写我自己的身份验证中间件。 我想创建一个自定义属性来附加到控制器操作(或整个控制器),指定它需要身份验证。 然后在请求期间,在我的中间件中,我想交叉引用需要身份验证的操作列表以及适用于此当前请求的操作。 我的理解是我在MVC中间件之前配置我的中间件,以便在管道中首先调用它。 我需要这样做,以便在MVC控制器处理请求之前完成身份验证,以便我不能防止在必要时调用控制器。 但是,这是否也意味着MVC路由器还没有确定我的路线呢? 在我看来,路线的决心以及路线行动的执行发生在管道的右侧?
如果我希望能够确定请求是否与控制器处理请求之前发生的中间件管道中的控制器操作相匹配,那么我将不得不编写自己的url解析器来解决这个问题吗? 在控制器实际处理请求之前是否有某种方法可以获取请求的路由数据?
编辑:我开始认为RouterMiddleware可能是我正在寻找的答案。 我假设我可以弄清楚如何让我的路由器选择与标准MVC路由器使用的路由相同的路由(我使用属性路由),并让我的路由器(真正的身份验证器)将请求标记为未成功进行身份验证时处理默认的mvc路由器执行实际的请求处理。 我真的不想完全实现MVC中间件所做的一切。 努力解决问题。 RouterMiddleware有点让我想起我需要做的事情。
编辑2:这是ASP.NET 5中的中间件模板
public class TokenAuthentication
{
private readonly RequestDelegate _next;
public TokenAuthentication(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
//do stuff here
//let next thing in the pipeline go
await _next(context);
//do exit code
}
}
最后,我查看了ASP.NET源代码(因为它现在是开源的!),并发现我可以从这个类复制UseMvc扩展方法,并替换掉我自己的默认处理程序。
public static class TokenAuthenticationExtensions
{
public static IApplicationBuilder UseTokenAuthentication(this IApplicationBuilder app, Action<IRouteBuilder> configureRoutes)
{
var routes = new RouteBuilder
{
DefaultHandler = new TokenRouteHandler(),
ServiceProvider = app.ApplicationServices
};
configureRoutes(routes);
routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
routes.DefaultHandler,
app.ApplicationServices));
return app.UseRouter(routes.Build());
}
}
然后你创建你自己的这个类的版本。 在我的情况下,我实际上并不想调用这些动作。 我会让典型的Mvc中间件做到这一点。 既然是这样的话,我将所有相关的代码都拿出来,并保留我需要获取actionDescriptor变量中的路由数据的内容。 我可能可以删除处理备份路由数据的代码,因为我不认为我会做什么会影响数据,但我已将它保留在示例中。 这是我将基于mvc路由处理程序开始的框架。
public class TokenRouteHandler : IRouter
{
private IActionSelector _actionSelector;
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
EnsureServices(context.Context);
context.IsBound = _actionSelector.HasValidAction(context);
return null;
}
public async Task RouteAsync(RouteContext context)
{
var services = context.HttpContext.RequestServices;
EnsureServices(context.HttpContext);
var actionDescriptor = await _actionSelector.SelectAsync(context);
if (actionDescriptor == null)
{
return;
}
var oldRouteData = context.RouteData;
var newRouteData = new RouteData(oldRouteData);
if (actionDescriptor.RouteValueDefaults != null)
{
foreach (var kvp in actionDescriptor.RouteValueDefaults)
{
if (!newRouteData.Values.ContainsKey(kvp.Key))
{
newRouteData.Values.Add(kvp.Key, kvp.Value);
}
}
}
try
{
context.RouteData = newRouteData;
//Authentication code will go here <-----------
var authenticated = true;
if (!authenticated)
{
context.IsHandled = true;
}
}
finally
{
if (!context.IsHandled)
{
context.RouteData = oldRouteData;
}
}
}
private void EnsureServices(HttpContext context)
{
if (_actionSelector == null)
{
_actionSelector = context.RequestServices.GetRequiredService<IActionSelector>();
}
}
}
最后,在管道末尾的Startup.cs文件的Configure方法中,我设置了它,以便为我的令牌认证和mvc路由器使用相同的路由设置(我使用属性路由)。
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//Other middleware delcartions here <----------------
Action<IRouteBuilder> routeBuilder = routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
};
app.UseTokenAuthentication(routeBuilder);
//Middleware after this point will be blocked if authentication fails by having the TokenRouteHandler setting context.IsHandled to true
app.UseMvc(routeBuilder);
}
编辑1:我还应该注意,目前我不担心需要额外的时间来选择两次,这是我认为会发生在这里,因为我的中间件和Mvc中间件都会这样做。 如果这成为一个性能问题,那么我会将mvc和身份验证构建到一个处理程序中。 这在性能方面是最好的想法,但我在这里展示的是我认为最模块化的方法。
编辑2:最后得到我需要的信息,我必须将ActionDescriptor转换为ControllerActionDescriptor。 我不确定你可以在ASP.NET中使用哪些其他类型的操作,但我非常确定我的所有操作描述符都应该是ControllerActionDescriptors。 也许旧的遗留Web Api东西需要另一种类型的ActionDescriptor。
链接地址: http://www.djcxy.com/p/28133.html上一篇: In ASP.NET 5, how do I get the chosen route in middleware?