Gre*_*obb 9 asp.net asp.net-mvc asp.net-mvc-routing asp.net-core-mvc
我正在构建一个ASP.NET 5(vNext)站点,它将托管动态页面,静态内容和REST Web API.我找到了如何使用新的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
}
}
Run Code Online (Sandbox Code Playgroud)
我最终查看了 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());
}
}
Run Code Online (Sandbox Code Playgroud)
然后您创建该类的您自己的版本。就我而言,我实际上不想调用这些操作。我会让典型的 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>();
}
}
}
Run Code Online (Sandbox Code Playgroud)
最后,在管道末尾的 Startup.cs 文件的配置方法中,我进行了设置,以便我对令牌身份验证和 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);
}
Run Code Online (Sandbox Code Playgroud)
编辑 1:我还应该注意,目前我并不担心选择两次路线所需的额外时间,这是我认为这里会发生的情况,因为我的中间件和 Mvc 中间件都会这样做。如果这成为一个性能问题,那么我会将 mvc 和身份验证构建到一个处理程序中。从性能角度来说,这将是最好的想法,但我在这里展示的是我认为最模块化的方法。
编辑2:最后为了获得我需要的信息,我必须将 ActionDescriptor 转换为 ControllerActionDescriptor。我不确定 ASP.NET 中还可以有哪些其他类型的操作,但我很确定我的所有操作描述符都应该是 ControllerActionDescriptors。也许旧的 Web Api 东西需要另一种类型的 ActionDescriptor。
归档时间: |
|
查看次数: |
3043 次 |
最近记录: |