In ASP.NET 5, how do I get the selected route in middleware?

I am creating an ASP.NET 5 (vNext) site that will host dynamic pages, static content, and REST web APIs. I found examples of how to create middleware using the new ASP.NET method, but I hit.

I am trying to write my own middleware for authentication. I would like to create a custom attribute to attach to controller actions (or entire controllers) that indicates that authentication is required for this. Then, during a request in my middleware, I would like to list a list of actions that require authentication, with the action that applies to this current request. As far as I understand, I configure my middleware before the MVC middleware so that it is called first in the pipeline. I need to do this so that authentication is done before the request is processed by the MVC so that I cannot prevent the controller from invoking if necessary. But does this not mean that the MVC router has not yet determined my route? It seems to me that the determination of the route and the implementation of this route action takes place at one step in the pipeline?

If I want to determine if the request corresponds to the action of the controller at the stage of the intermediate pipeline, which occurs before the request is processed by the controller, will I have to write my own custom URL analyzer to understand this? Is there a way to get the routing data for the request before it is processed by the controller?

Edit: I'm starting to think that RouterMiddleware might be the answer I'm looking for. I suppose I can understand how my router selects the same routes as the standard MVC router (I use attribute routing), and my router (really an authenticator) is marked as a request that is not processed when it successfully authenticates that the mvc router By default, it processes the actual request. I really don't want to fully implement everything that MVC middleware does. Working on trying to figure this out. The RouterMiddleware view shows me what I need to do, I think.

Edit 2: Here is a template for middleware in 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 } } 
+7
asp.net-mvc asp.net-core-mvc asp.net-mvc-routing
source share
1 answer

In the end, I looked at the source code of ASP.NET (because it is now open source!) And found that I can copy the UseMvc extension method from this class and replace the default handler for my own.

 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()); } } 

Then you create your own version of this class . In my case, I really don't want to reference actions. I will let the typical Mvc middleware do this. Since this is the case, I selected all the associated code and saved only what I need to get the route data that is in the actionDescriptor variable. I can probably delete the code associated with the backups of the route data, since I do not think that what I will do will affect the data, but I saved it in this example. This is the skeleton I'll start with, based on the mvc route handler.

 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>(); } } } 

And finally, in the Startup.cs Configure file at the end of the pipeline, I have a setting, so I use the same routing setting (I use attribute routing) to authenticate the token and router 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); } 

Edit 1: I should also note that at the moment I am not worried about the extra time required to select a route twice, which I think will happen here since both my middleware and Mvc middleware will do this. If this becomes a performance issue, I will create mvc and authentication for one handler. That would be the best idea in terms of performance, but what I showed here is the most modular approach that I think.

Edit 2: In the end, in order to get the information I needed, I had to include an ActionDescriptor in ControllerActionDescriptor. I'm not sure what other types of actions you can have in ASP.NET, but I'm sure all my action descriptors should be ControllerActionDescriptors. Perhaps the old legacy of Web Api requires a different type of ActionDescriptor.

+3
source share

All Articles