Customizing ASP.NET Core MVC: filters, constraints and conventions

ASP.NET Core – much like the not core predecessor – has many extension possibilities. In this post I’ll cover three of those possibilities that can be useful for application-wide and/or cross-cutting customization:

  • Filters (action filters).
  • Action constraints
  • Application model conventions.

This post is a condensed/revisited version of two posts I did last year about the application model on ASP.NET Core (part I and part II), which by that time was still on beta versions. If you are up to a more in-depth description, take a look at those two posts, as most of them still applies.

Action filters

ASP.NET Core MVC includes multiple types of filters, namely authorization, action and exception filters. All the filters extend IFilterMetadata, which is a marker interface used by the framework to discover filters applied as custom attributes.

The filters are executed within the so called action invocation pipeline or filter pipeline, which runs after MVC selects an action method to handle the request. Different types of filters execute at different stages in the pipeline, hence having different intended usages The official documentation has a decent guide on selecting the appropriate filter type. For this post I’ll stick with action filters.

Actions filters execute immediately before and/or after the action method. They have access to the results of model binding and action parameters (before the action executes) as well as the action result (after the action executes). These filters are represented by the IActionResult interface but the framework includes an utility base class for attribute action filters named ActionFilterAttribute.

A common scenario on action methods (namely Web APIs) is to check if the model state is valid and, if not, return a 400 status code. This is a rather cross-cutting concern and can be easily implemented via an action filter. Using the aforementioned ActionFilterAttribute base class, we can  write a filter that checks the model state validation status before the action method is executed:

public class ValidateModelState : ActionFilterAttribute
{
  public override void OnActionExecuting(ActionExecutingContext context)
  {
    if (!context.ModelState.IsValid)
    {
        context.Result = new BadRequestResult();
    }
  }
}

By setting the Result property during the OnActionExecuting method we prevent the execution of the action method and subsequent action filters. The filter can then be applied to an action method (or a controller class) just like any other custom attribute:

[HttpPost, ValidateModelState]
public IActionResult Test([FromBody] Model model)
{
  // ...
}

Action constraints

An important part of request handling is determining which action method should be executed. This might depend on different factors, such as the request’s HTTP method. Action constraints provide a means of determining if an action is suitable for a given request. They are represented by the IActionConstraint interface, depicted below. The context parameter can be used to access the route data, the HttpContext and the current candidate actions.

public interface IActionConstraint : IActionConstraintMetadata
{
    int Order { get; }
    bool Accept(ActionConstraintContext context);
}

As an example, lets say I want to have an API endpoint that accepts POST requests with two different media types. Instead of if/else on a single action method, we can add an action constraint based on the request’s content type. Here’s how I’d like to write my actions:

[HttpPost, MediaType("application/json")]
public void MyAction(MyModel model) { }

[HttpPost, MediaType("image/png")]
public void MyAction(Stream content) { }

Implementing MediaTypeAttribute  is pretty straight forward: the framework already includes a base class for action constraints implemented as attributes, which we can use in this case:

  public class MediaTypeAttribute : ActionMethodSelectorAttribute
  {
      private readonly string _mediaType;

      public MediaTypeAttribute(string mediaType)
      {
          this._mediaType = mediaType;
      }

      public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
      {
          return routeContext.HttpContext.Request.ContentType.Equals(_mediaType, StringComparison.OrdinalIgnoreCase);
      }
  }

Application model conventions

If you’re familiar with previous versions of ASP.NET MVC you probably have heard about action descriptors. Action descriptors are discovered by MVC during application startup and represent the HTTP endpoints on the application. As with many other items on MVC, action descriptors are obtained via their own set of providers. While it is possible to add/replace IActionDescriptorProviders, the default implementation includes an intermediate mechanism to enable customization of action descriptors on most scenarios: the ApplicationModel.

The application model is a representation of the application components discovered by the framework, namely controllers and their actions. As illustrated below, this model includes a lot of metadata on the different components. Note that the aforementioned filters and action constraints are also part of the application model.

mvc-appmodel

After the whole model is discovered by the framework, we’re given a chance to customize it before the final action descriptors are created. This customizations are applied using application model conventions:

public interface IApplicationModelConvention
{
    void Apply(ApplicationModel application);
}

On previous versions of Web API, one could prefix action methods with the HTTP method name to make them apply to that method (e.g. GetNNNN, PostNNNN). This convention is not present on ASP.NET Core MVC, but the behavior is easy to mimic using an application model convention.

public class HttpMethodActionPrefixConvention : IApplicationModelConvention
{
    public void Apply(ApplicationModel application)
    {
        var actions = application.Controllers.SelectMany(c => c.Actions);
        foreach (var action in actions)
        {
            if (action.ActionName.StartsWith("get", StringComparison.OrdinalIgnoreCase))
            {
                var selector = action.Selectors.First();
                selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { HttpMethod.Get.Method }));
                action.ActionName = action.ActionName.Substring(3);
            }
            // ...
        }
    }
}

In this example, for each action that was collected into the application model I’m checking if its name starts with an HTTP method. If so, a constraint is added to the ActionModel and the final action name is adjusted. This way, if we have a GetData action it will be constrained to HTTP GET and the action name throughout the application (e.g. route matching) will be Data. Finally, to use the convention we need to add it to MVC options in the Startup class:

services.AddMvc(options =>
{
    options.Conventions.Add(new HttpMethodActionPrefixConvention());
});

As a final note, one might ask why the application model was introduced, instead of having the developers directly modify action descriptors. The application model provides a customization model that is closer to how a developer organizes the application (e.g. controllers that contain actions). The final action descriptors are more complex and there’s a lot of plumbing to be done to create them that probably shouldn’t surface to the developer. For instance, a single ActionModel might result on multiple ActionDescriptors, depending on attribute routing. The application model is simpler and easier to modify, leaving all the final hard work to the framework.

This post ended up quite long, but it made sense to me to describe the three mechanisms together. Hope this helps!

Advertisement

Cookie-based TempData for ASP.NET Core

When developing web applications, it is common to persist some small state across a POST-redirect-GET flow (e.g. feedback message to a user after some action). In ASP.NET, TempData is the appropriate mechanism for this scenario: it persists a value until the next time it is fetched.

The default TempData implementation on ASP.NET MVC 5 and ASP.NET Core MVC is based on session storage. Another approach is to use a cookie as storage, making a client’s TempData flow with him. On ASP.NET MVC 5 I’ve been successfully using Brock Allen’s CookieTempData provider for the last few years. However, it doesn’t support ASP.NET Core.

I’ve implemented a cookie-based TempData provider for ASP.NET Core MVC. The code is available on GitHub and the library is published as a NuGet package. To replace the default provider with the cookie-based one, you just need to add a call to AddCookieTempData on your ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddCookieTempData();
}

The cookies are serialized in BSON and protected using the new data protection APIs. Feel free to provide feedback. Hope this helps!

ASP.NET Core hosting (revisited) – Part II

Last year I did a series of posts following ASP.NET Core execution in depth. At that time the framework was still named ASP.NET 5, I was using betas 5/6 and DNX (.NET Execution Environment) was still around. A lot has changed since then, namely the usage of .NET CLI instead of DNX. Today I’ll continue my previous post on hosting and somehow revisit last year’s post about ASP.NET request processing.

The last post ended up right before the WebHost is initialized. This is where ASP.NET configures the application services and middlewares in order to build the final RequestDelegate. Finally, when started, the host fires up the registered server (IServer), linking the incoming requests to the RequestDelegate. Lets go through these steps.

The initialization of WebHost is where the Startup class plays its role. Internally, the startup logic is represented by the IStartup interface, depicted below:

public interface IStartup
{
    IServiceProvider ConfigureServices(IServiceCollection services);
    void Configure(IApplicationBuilder app);
}

This interface mimics the two application startup actions: 1) configure services on DI and 2) configure the middlewares. On the Main method, we invoked UseStartup<T>; if you take a look at the method’s source code you’ll see that it actually registers an implementation of IStartup that resolves the two actions by convention, by looking up methods on the supplied startup class.

Anyway, the first actions on the WebHost initialization are all about gathering the required services and configuring the application services (the following lines are a bit scattered on the source; I condensed the most relevant parts here):

_startup = _hostingServiceProvider.GetRequiredService<IStartup>();
_applicationServices = _startup.ConfigureServices(_applicationServiceCollection);
Server = _applicationServices.GetRequiredService<IServer>();
var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();
IApplicationBuilder builder = builderFactory.CreateBuilder(Server.Features);

Notice how the host gets required services that were previously configured by the host builder and our code on the Main method.

At this point the application services are configured on the DI infrastructure and the host has an application builder ready to start configuring middlewares. There are three main players on that stage:

  • IApplicationBuilder – used to register middlewares. The interface is very simplistic:
    public interface IApplicationBuilder
    {
        // ...
        RequestDelegate Build();
        IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
    }
  • The Startup.Configure method – where the application configures its middlewares. To that end, it gets an IApplicationBuilder as an argument, as shown below:
    public class Startup
    {
        public void Configure(IApplicationBuilder app) { ... }
    }
  • Startup filters – classes that implement IStartupFilter, allowing configurations to be done on the IApplicationBuilder before the Startup.Configure method is invoked. The interface is as follows:
    public interface IStartupFilter
    {
        Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next);
    }

The only usage of these filters I found on ASP.NET source is to register a middleware that handles the per-request lifetime on the DI infrastructure. It’s important to ensure that this middleware executes before application middlwares so that the latter can use per-request services.

The construction of the aplication RequestDelegate starts exactly by applying startup filters. The bootstrap Action<IApplicationBuilder> is the Startup.Configure method and filters successively wrap around the previous action. Note that filters are applied in reverse order of registration, so that the first to be registered is actually the outermost wrapper (the first to execute).

var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>();
Action<IApplicationBuilder> configure = _startup.Configure;
foreach (var filter in startupFilters.Reverse())
{
    configure = filter.Configure(configure);
}

Having this composition of middleware configuration actions, it’s time to actually execute them on the IApplicationBuilder and then build the RequestDelegate.

configure(builder);
return builder.Build();

After the configure delegate is executed, the application builder contains and ordered list of middlewares, as shown below. For this example I have two middlewares on my Startup class; the other is the aforementioned middleware that handles per-request DI.

middlewares

Recall that a middleware is a function that composes a RequestDelegate around another (the next one in the chain). Therefore, the final RequestDelegate must be built from the last middleware to the first, as illustrated below. This ensures that the first middleware to be registered is actually the outermost wrapper (the first to execute).

Untitled.png

The application builder’s Build method does exactly that: reverse the middlewares list and invoke them one by one, passing the current RequestDelegate. To bootstrap the configuration, it uses a default RequestDelegate that returns an HTTP 404 status code, ensuring a default response when no other middleware handles the request.

public RequestDelegate Build()
{
    RequestDelegate app = context =>
    {
        context.Response.StatusCode = 404;
        return Task.FromResult(0);
    };
    foreach (var component in _components.Reverse())
    {
        app = component(app);
    }
    return app;
}

At this point the WebHost is initialized and we have a RequestDelegate that represents the whole application. When the host is started (its Run method is invoked on the console application) it’s time to bring the bring the server implementation in and have it deliver requests to the RequestDelegate. To that end, one last component is used: IHttpApplication.

public interface IHttpApplication<TContext>
{
    TContext CreateContext(IFeatureCollection contextFeatures);
    Task ProcessRequestAsync(TContext context);
    void DisposeContext(TContext context, Exception exception);
}

This interface is the glue between the server and our application. This is visible on the IServer interface:

public interface IServer : IDisposable
{
    void Start<TContext>(IHttpApplication<TContext> application);
    // ...
}

Just by analyzing these two interfaces we can imagine a skeleton of what’s happening under the covers, probably with some actions in between:

  1. When the server is started, it is given an IHttpApplication.
  2. When a new request arrives on the server, it asks the application  to create a context for the request.
  3. The context is supplied to the application for request processing. This means that the context must contain everything the application needs to handle the corresponding request.
  4. After the request is done, the server asks the application to dispose of the previously created context.

So, what’s that IFeatureCollection thing on CreateContext? Features are fine-grained representations of the capabilities exposed by the server for the current request. There are feature interfaces to represent the HTTP connection, the request, the response, and so on. You can take a look at all the features on the HttpAbstractions repository.

If you recall, the RequestDelegate gets an HttpContext and returns a Task. It is the hosting layer’s responsibility to wrap the low-level feature interfaces exposed by the server in a higher-level, strongly-typed HttpContext. This is done via the built-in implementation of IHttpApplication:

private readonly RequestDelegate _application;
private readonly IHttpContextFactory _httpContextFactory;

public Context CreateContext(IFeatureCollection contextFeatures)
{
    var httpContext = _httpContextFactory.Create(contextFeatures);
    // ...
    return new Context
    {
        HttpContext = httpContext,
        // ...
    };
}

public Task ProcessRequestAsync(Context context)
{
    return _application(context.HttpContext);
}

Going back a bit, when we start a WebHost, it actually starts the underlying server passing in the IHttpApplication and everything is ready for request processing. When a request comes in and the server invokes IHttpApplication.ProcessRequestAsync, the HttpContext is passed into the application RequestDelegate and we’re immediately executing middlewares’ code. There’s literally nothing else on the way of the request! Pretty slim, right?

execution

As a final note – and in case you’re wondering – it’s worth mentioning that HttpContext itself is an abstraction for applications to build on. The IHttpContextFactory implementation creates instances of DefaultHttpContext, which in turn uses DefaultHttpRequest and DefaultHttpResponse. These classes contain the logic of mapping feature interfaces into the high-level APIs.

The two sets of abstractions – feature interfaces and HTTP abstractions – allow applications to be hosted on different servers and different servers to be used without being tied to one specific hosting model, as illustrated below.

abstractions.PNG

And that’s it for this series. I revisited the execution of an ASP.NET Core application, starting from configuration via WebHostBuilder up to the moment where a request is handled to the application RequestDelegate. Hope this helps!

ASP.NET Core hosting (revisited) – Part I

Last year I did a series of posts following ASP.NET Core execution in depth. At that time the framework was still named ASP.NET 5, I was using betas 5/6 and DNX (.NET Execution Environment) was still around. A lot has changed since then, namely the usage of .NET CLI instead of DNX. Today I’ll revisit last year’s post about ASP.NET hosting.

Prior to RC2, an ASP.NET Core application was a class library. DNX – the execution environment at that time – was responsible for providing support for project.json, loading assemblies and invoking the ASP.NET hosting libraries, which in turn would find a Startup class to configure and start the application. Just for completeness, we used to have something like this on the commands section of project.json:

"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"

With the release of RC2, DNX and the related tools were replaced by the .NET CLI and, as such, ASP.NET Core applications became regular console applications. Instead of using the hosting libraries as the entry point and have them discover our application, we now call into ASP.NET libraries from our Main method (the common entry point) to configure the web application and fire up ASP.NET. Here’s an example of such a Main method:

public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseStartup<Startup>()
                .Build();
            host.Run();
        }

The code is straightforward: we configure the server (Kestrel, in this case) and the Startup class (this now has to be done explicitly) to build the web host. Then we run it, blocking the main thread until the application shuts down. For the remainder of this post (and the following) I’ll go through the ASP.NET code to see what’s happening under the covers when we configure and run the web host.

Before moving further it’s worth doing a quick recap on some terminology:

  • RequestDelegate – represents the handling of a request. It’s a function that gets a context and returns a Task that completes when the request has been processed. This simple delegate actually represents the whole application.
    public delegate Task RequestDelegate(HttpContext context);
  • Middleware – a component that participates on request processing by inspecting, routing, or modifying request and/or response messages. A simplistic view of a middleware is a Func<RequestDelegate, RequestDelegate>, i.e. a means of composing around a given request delegate. This composition approach replaces the event-driven model found on previous versions of ASP.NET.
  • Startup class – the class where you configure the services and middlewares of your application. The configuration methods are discovered by convention, as described on ASP.NET documentation. You can also check this post for a more detailed explanation.

The interface of WebHostBuilder is rather simple, as illustrated below. Configuring the builder is mostly about configuring services for the DI infrastructure.

    public interface IWebHostBuilder
    {
        IWebHost Build();
        IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices);
        IWebHostBuilder UseSetting(string key, string value);
        // ...
    }
    public class WebHostBuilder : IWebHostBuilder 
    {
        private readonly List<Action<IServiceCollection>> _configureServicesDelegates;

        public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices)
        {
          _configureServicesDelegates.Add(configureServices);
          return this;
        }
        // ...
    }

Most of the builder configurations done on the Main method are exposed via extension methods on the corresponding packages. For instance, the UseKestrel method is found on the Kestrel server package and looks like this:

public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder)
{
    return hostBuilder.ConfigureServices(services =>
    {
        services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
        services.AddSingleton<IServer, KestrelServer>();
    });
}

Notice the registration of IServer. This is (as expected) a required service that represents the server and will be used later on to link incoming requests to the application. If we were to use another server implementation, it would likely register its own IServer class via a similar extension method.

The WebHostBuilder.Build method is where everything is assembled. It has two main responsibilities:

  1. Configure and register the services needed for hosting.
  2. Create and initialize the WebHost.
public IWebHost Build()
{
    var hostingServices = BuildHostingServices();
    var hostingContainer = hostingServices.BuildServiceProvider();
    var host = new WebHost(hostingServices, hostingContainer, _options, _config);
    host.Initialize();
    return host;
}

The BuildHostingServices method is where hosting-related configuration takes place. First, the IHostingEnvironment is configured. This service can be used from this moment on to get information about the physical environment where the application is hosted, such as the root path for static content.

_options = new WebHostOptions(_config);

var appEnvironment = PlatformServices.Default.Application;
var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, appEnvironment.ApplicationBasePath);
var applicationName = _options.ApplicationName ?? appEnvironment.ApplicationName;

_hostingEnvironment.Initialize(applicationName, contentRootPath, _options);

var services = new ServiceCollection(); // (1)
services.AddSingleton(_hostingEnvironment);

Note the creation of the service collection (1) that is used to create the base DI service provider for the WebHost (on the Build method shown before).

The next relevant action is the registration of some important built-in services that will be used for application configuration and request processing. We’ll go through the usage of these services later on.

services.AddLogging();
services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
services.AddTransient<IHttpContextFactory, HttpContextFactory>();
services.AddOptions();
  • IApplicationBuilderFactory – allows create of the application builder that is used by the Startup class to configure middlewares.
  • IHttpContextFactory – create the HttpContext for an incoming request.
  • AddOptions – adds support for the options configuration pattern.

The last step is to execute the service configuration delegates that were externally registered in the WebHostBuilder. This is the case of the delegates registered by UseKestrel and UseStartup methods, as shown before.

private readonly List<Action<IServiceCollection>> _configureServicesDelegates;
// ...
foreach (var configureServices in _configureServicesDelegates)
{
    configureServices(services);
}

At this point all the base configurations and services required by the host are in place. The Build method can now create a WebHost instance and initialize it. This where all the application-specific configurations are triggered and it will be the subject of my next post. Stay tuned!

ASP.NET Core MVC Application Model – Part II

EDIT 03/08/2016: this post was originally written for ASP.NET MVC 6 beta versions. While most of the post and its general idea still applies to ASP:NET Core MVC, some details may not be valid anymore and links might be broken.


On the previous post of this series I introduced the ApplicationModel, its main types and how it can be used to customize the final action descriptors. On this post I’ll go into a couple code samples to make everything clearer.

On previous versions of Web API, one could prefix action methods with the HTTP method name to make them apply to that method (e.g. GetNNNN, PostNNNN). This convention is not present on MVC Core, but the behavior is easy to mimic using an application model convention.

public class HttpMethodActionPrefixConvention : IApplicationModelConvention
{
    public void Apply(ApplicationModel application)
    {
        var actions = application.Controllers.SelectMany(c => c.Actions);
        foreach (var action in actions)
        {
            if (action.ActionName.StartsWith("Get"))
            {
                action.HttpMethods.Add("GET");
                action.ActionName = action.ActionName.Substring(3);
            }
            // …
        }
    }
}

As I mentioned on the previous post, a convention is a type that implements IApplicationModelConvention. In the example above, I’m iterating all the actions that were collected into the application model and checking if their names start with an HTTP method. If so, that HTTP method is added to the ActionModel HTTP methods and the final action name is adjusted. This way, if we have a GetData action it will be constrained to HTTP GET and the action name throughout the application (e.g. route matching) will be Data.

Finally, the convention is added to MVC options on the Startup class:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvcCore(options =>
    {
        options.Conventions.Add(new HttpMethodActionPrefixConvention());
    });
}

Besides conventions, one might also use the different types that are considered by the framework when building the application model (e.g. IActionConstraintMetadata, IFilterMetadata). For this post I’ll stick with IActionConstraintMetadata which is a means of determining if an action is suitable for a given HTTP request.

Recalling the previous post, the default application model provider searches whether attributes applied to action methods implement specific types, namely IActionConstraintMetadata. However, this is a marker interface, meaning there must be something more to it. Looking into ActionDescriptor we can see that it still has a collection of IActionConstraintMetadata. If we keep going up we’ll end-up on action selection. At this point, the IActionConstraintMetadatas are processed to obtain IActionConstraints, which actually determine if an action is suitable for a request, as illustrated below. The context provides access to things such as the HttpContext, the candidate ActionDescriptors for the current request and the ActionDescriptor being evaluated.

public interface IActionConstraint : IActionConstraintMetadata
{
   int Order { get; }
   bool Accept(ActionConstraintContext context);
}

Taking another look at the DefaultActionSelector one can see that IActionConstraintMetadatas are processed by – guess what? – another set of providers. As with other providers so far, there’s a default implementation that should suite all our needs. Here an excerpt from its source code:

var constraint = item.Metadata as IActionConstraint;
if (constraint != null)
{
    item.Constraint = constraint;
    return;
}

var factory = item.Metadata as IActionConstraintFactory;
if (factory != null)
{
    item.Constraint = factory.CreateInstance(services);
    return;
}

This means that, by default, for a given IActionConstraintMetadata there are two options:

  1. It is an IActionConstraint – The instance can be used directly. This is good enough for most action constraints, since the Accept method gets contextual information.
  2. It is an IActionConstraintFactory – The factory is invoked passing the services locator. Note that a IActionConstraintMetadata is typically a custom attribute, which means it has limited flexibility on construction. If the constraint you’re implementing depends on external services, you can use the factory approach to create it.

At this point, all that’s left is an example. Lets say we want to have an API endpoint that accepts POST requests with two different media types. Instead of if/else on a single action method, we can add an action constraint based on the request’s content type. Here’s how I’d like to write my actions:

[HttpPost, MediaType("application/json")]
public void MyAction(MyModel model)
{
}

[HttpPost, MediaType("image/png")]
public void MyAction(Stream content)
{
}

Implementing MediaTypeAttribute  is pretty straight forward: the framework already includes a base class for attributes that are action constraints, which we can use in this case:

public class MediaTypeAttribute : ActionMethodSelectorAttribute
{
    private readonly string mediaType;

    public MediaTypeAttribute(string mediaType)
    {
        this.mediaType = mediaType;
    }

    public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
    {
        return routeContext.HttpContext.Request.ContentType == this.mediaType;
    }
}

And that’s it! On these two posts I covered a lot about application model, how it’s built and how we can use it to customize the final action descriptors on our MVC 6 applications. Hope this helps!

ASP.NET Core MVC Application Model – Part I

EDIT 03/08/2016: this post was updated to reflect naming changes from MVC 6 to ASP.NET Core MVC. All the contents still apply.


On my previous posts about routing on ASP.NET 5 I mentioned how action descriptors are used to build routes for attribute routing. Action descriptors represent the HTTP endpoints on the application and, as expected, they are also used when selecting an action for a given request.

The collection of action descriptors that are used by the framework is obtained via a set of IActionDescriptorProvider. While it is possible to add/replace IActionDescriptorProviders, the built-in implementation includes another mechanism to enable customization of action descriptors on most scenarios: the ApplicationModel.

The application model is a representation of the application components discovered by the framework, namely controllers and their actions. As illustrated below, this model includes a lot of metadata on the different components.

application_model

The application model is also created by a set of providers, represented by the IApplicationModelProvider interface. Again, it’s not very likely that you need to replace/extend these providers, since the application model is made available for customization (more on this soon). It’s also worth pointing that, in order to build the application model, the framework needs to find the controller types available on the application. The lookup is based on IControllerTypeProvider and can also be configured as needed. This post describes controller discovery in more detail.

Back to the application model diagram, lets go into some of the types and how they are used by the default application model provider:

  • ControllerModel – represents a controller that was discovered by the application model provider. An instance of this class is created for each controller that is discovered by the aforementioned IControllerTypeProvider. This is also when the controller names are defined.
  • ActionModel – represents an action of a controller. An instance of this class is created for each eligible action on a controller. There are multiple requirements for a method to become an action, such as being public, non-abstract and not inherited from object.
  • IFilterMetadata – marker interface to signal a filter. Filters can be applied at application (global), controller or action level. The default application model provider searches for filters metadata on the custom attributes of controller and action types. The final set of filters on each action is assembled later on, when building the action descriptors.
  • IRouteConstraintProvider – also specified via custom attributes, this interface can be used to specify constraints for a given route value (e.g. area=xpto).
  • IActionConstraintMetadata – marker interface for action constraints, i.e. types that will help determine if an action is suitable for a given HTTP request. I’ll exemplify their usage on the second post of this series.

I previously mentioned that the application model is a means of customizing action descriptors, but that’s not completely accurate. In fact, that customization is supported by the default IActionDescriptorProvider using application model conventions, which are applied to the application model:

internal protected IEnumerable<ControllerActionDescriptor> GetDescriptors()
{
    var applicationModel = BuildModel();
    ApplicationModelConventions.ApplyConventions(applicationModel, _conventions);
    return ControllerActionDescriptorBuilder.Build(applicationModel);
}

As shown on the snippet above, a set of conventions is applied to the application model before it is used to build the final action descriptors. A convention is represented by the IApplicationModelConvention interface and has access to the whole application model for modification:

public interface IApplicationModelConvention
{
  void Apply(ApplicationModel application);
}

One might ask why the application model was introduced, instead of having the developers directly customize action descriptors. Despite not being sure about the reasons, I’d say the ASP.NET team wanted to provide a customization model that is closer to how a developer organizes the application (e.g. controllers that contain actions). The final action descriptors are more complex and there’s a lot of plumbing to be done to create them that probably shouldn’t surface to the developer. For instance, a single ActionModel might result on multiple ActionDescriptors, depending on attribute routing. The application model is simpler and easier to modify, leaving all the final hard work to the framework.

On the following post I’ll bring up some code exemplifying the usage of conventions and some other interfaces that are present on the application model. Stay tuned!

ASP.NET 5 Configuration

One of the many changes introduced by ASP.NET 5 is the new configuration framework. Actually, the new configuration packages are not specific to ASP.NET; they are a generic way of accessing key/value settings which can be loaded from different sources (e.g. JSON files, command-line arguments).

The configuration topic is not very broad and a lot has already been written, namely this blog post and the official ASP.NET documentation. As a recap, here’s an example of how to create a Configuration instance from JSON files and environment variables. Note that order is relevant (values from later sources win).

public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup(IHostingEnvironment env, IApplicationEnvironment app)
    {
        var configuration = new ConfigurationBuilder(app.ApplicationBasePath)
            .AddJsonFile("config.json")
            .AddJsonFile($"config.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();

        Configuration = configuration.Build();
    }

    public void Configure(IApplicationBuilder app)
    {
        // Use Configuration here
    }
}

As a side note, the configuration packages suffered some reorganizations on beta 5. The example above is based on beta 7.

The infamous Web.config files are (practically) gone and the different middlewares aren’t implicitly getting their configurations from some configuration section. In fact, as far as I can tell, configurations are explicit via code on the application’s Startup.Configure method. The simple configuration framework can help you do the rest.

One common configuration scenario is having a few related application settings and wanting to get them inject into, lets say, a controller. When using Web.config files one probably would use the System.Configuration classes and define a type for a custom configuration section. The thing is: using System.Configuration is soooo painful. Of course one could use an add-in for VS or other simpler approaches, but it’s always bothered me that there wasn’t a straightforward way of having simple application settings bound to a dedicated configuration section out of the box.

One of the best things that comes with the new configuration system is the Options package. It builds on top of the configuration packages and adds support for easily representing settings using POCOs and have them registered on the DI infrastructure. Here’s an example.

Let’s say the JSON config file registered on the previous example has the following contents:

{
  "AppSettings": {
    "SiteTitle": "Awesome site"
  }
}

We can define a POCO to represent “AppSettings”:

public class AppSettings
{
    public string SiteTitle { get; set; }
}

And then, on our Startup class we just need to add the Options services and use the Configure extension method, which is part of the options package:

public void ConfigureServices(IServiceCollection services)
{
    services.AddOptions(); // AddMvc() actually adds this internally
    services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
    // ...
}

This binds the settings to an instance of AppSettings and registers it on the DI infrastructure. To access the settings from a controller (or any other class) the IOptions interface is used, as illustrated below.

public class HomeController
{
    private readonly AppSettings settings;

    public HomeController(IOptions<AppSettings> settings)
    {
        this.settings = settings.Options;
    }

    // ...
}

Pretty straightforward, right? As usual, I’ll go into some details on how the options classes are registered by the Configure extension method above.

Starting on the Configure method itself, we can see that it creates an instance of ConfigureFromConfigurationOptions with the given Configuration and registers it.

public static IServiceCollection Configure<TOptions>(
    this IServiceCollection services,
    IConfiguration config)
{
    services.ConfigureOptions(new ConfigureFromConfigurationOptions<TOptions>(config));
    return services;
}
public static IServiceCollection ConfigureOptions(
    this IServiceCollection services,
    object configureInstance)
{
    var serviceTypes = FindIConfigureOptions(configureInstance.GetType());
    foreach (var serviceType in serviceTypes)
    {
        services.AddInstance(serviceType, configureInstance);
    }
    return services;
}

The ConfigureOptions method above seems more interesting. It does the following:

  1. Find concretizations of IConfigureOptions<T> on the given instance’s type. In our case, we saw that an instance of ConfigureFromConfigurationOptions is used. If we follow the type’s inheritance hierarchy, we can see that it implements IConfigureOptions for the given options type (AppSettings, in our case).
  2. Registers all the implemented configure options interfaces on the DI infrastructure, having them resolved to the given instance.

The IConfigureOptions<TOptions> interface is very simple:

public interface IConfigureOptions<in TOptions>
{
    void Configure(TOptions options);
}

Note that it gets an instance of the options and should mutate it as needed. If you’re wondering why, I’ll get into that in a moment.

So, at this point the DI container knows that our ConfigureFromConfigurationOptions<AppSettings> should be used to configure AppSettings from the given Configuration section. It is a good time to point that this class uses a ConfigurationBinder to bind the configuration keys/values to the options properties.

configure_options

As we previously discussed, IOptions<T> is used to access the option instances. How do we get there? The answer lies on the AddOptions method, used to enable the options framework:

public static IServiceCollection AddOptions(IServiceCollection services)
{
    services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
    return services;
}

Ok, so this binds (on DI) all the concretizations of IOptions<T> to the corresponding OptionsManager<T>, which is the bridge between IConfigureOptions and IOptions:

public class OptionsManager<TOptions> : IOptions<TOptions>
{
  public OptionsManager(IEnumerable<IConfigureOptions<TOptions>> setups)
  {
    _setups = setups;
  }

  // ...
}

This class gets a set of IConfigureOptions for its options type (resolved by DI), meaning that you can call the Configure method multiple times, hence registering multiple IConfigureOptions on DI. This is why the IConfigureOptions interface mutates an existing instance: it may be working over an object that already has some (or all) of its properties populated. This allows overriding/combining values from different actions/sources, just like it is done on the Configuration classes.

The configuration logic just described is implemented on the Options property of the manager, as illustrated below. Note that the configuration is executed only once. In addition, the OptionsManager was registered as a singleton on DI, which means there’s a single IOptions instance for each options type in the application.

public virtual TOptions Value
{
    get
    {
        if (_options == null)
        {
            _options = _setups == null
                ? new TOptions()
                : _setups.Aggregate(new TOptions(),
                                    (options, setup) =>
                                    {
                                        setup.Configure(options);
                                        return options;
                                    });
        }
        return _options;
    }
}

Here’s a more complete diagram of the types in use:

options

I’d prefer an approach where the controller dependency was directly AppSettings. At a glace, I think it would have been possible to avoid IOptions<T> but, well, it works this way too.

ASP.NET 5 Routing – Part II

On the previous post of this series I covered the basics of ASP.NET 5 routing. On this post I’ll explore how MVC fits on the routing system.

When configuring an application, the UseMvc method is invoked to add MVC to the pipeline. This method allows us to configure convention-based routes, such as:

app.UseMvc(routes =>
{
routes.MapRoute(
null,
"Hello/{name:alpha}",
new { controller = "HelloWorld", action = "Index" });
});

The method takes an action of IRouteBuilder which is just a means of aggregating routes and creating a RouteCollection. The MapRoute extension method is a simplified way of creating a TemplateRoute.

The same routing configuration could be achieved using attribute routing by annotating  the controller/action:

public class HelloWorldController : Controller
{
[Route("Hello/{name:alpha}")]
public object Index(string name)
{
return Content("Hello " + name + " from the ASP.NET 5 world!");
}
}

The UseMvc method is responsible for wiring up all the routes and it’s pretty straight forward:

public static IApplicationBuilder UseMvc(
[NotNull] this IApplicationBuilder app,
[NotNull] Action<IRouteBuilder> configureRoutes)
{
// ...

var routes = new RouteBuilder
{
DefaultHandler = new MvcRouteHandler(),
ServiceProvider = app.ApplicationServices
};

configureRoutes(routes);

routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
routes.DefaultHandler,
app.ApplicationServices));

return app.UseRouter(routes.Build());
}

A few things to highlight:

  1. The route builder is created using MvcRouteHandler as the default handler. This is MVC’s top-most IRouter, which is used as the inner router on all the routes being created.
  2. Attribute routes are added on the beginning of the route list, which means they have precedence over convention-based routes, as expected. The AttributeRouting class is used to build an IRouter that composes all the attribute routes. This class lives in the MVC namespace, i.e. it’s not part of the base Microsoft.AspNet.Routing package.
  3. After building the application route collection, the basic routing middleware is registered, as described on the previous post.

Even if we didn’t go further, it’s already clear how MVC fits on the routing system by leveraging IRouter and the routing middleware (UseRouter). The next step is to take a look on how the attribute-based routes are created.

The AttributeRouting class just creates an instance of AttributeRoute which is an IRouter responsible for building the attribute routes. This is done on the first time AttributeRoute is invoked:

public Task RouteAsync(RouteContext context)
{
var route = GetInnerRoute();
return route.RouteAsync(context);
}

private InnerAttributeRoute GetInnerRoute()
{
var actions = _actionDescriptorsCollectionProvider.ActionDescriptors;
if (_inner == null || _inner.Version != actions.Version)
{
_inner = BuildRoute(actions);
}

return _inner;
}

The BuildRoute method is where the routes are actually processed; it executes the following steps:

  1. Extract route information from the available action descriptors (more on this further on).
  2. Create data structures for link generation based on route values (note that the routing system is bidirectional).
  3. Create data structures for URL matching.
  4. Create an instance of InnerAttributeRoute, which is the actual IRouter for attribute routing (more on this further on).

The first step is based on ActionDescriptor which represents an action on your application that was discovered by the framework. Building this model is a whole new subject, but for this post suffices to say that it contains things like the action name, metadata of the action parameters and attribute routing metadata, if applicable:

public class ActionDescriptor
{
public virtual string Name { get; set; }
public IList<ParameterDescriptor> Parameters { get; set; }
public AttributeRouteInfo AttributeRouteInfo { get; set; }

// ...
}

For each action that has attribute route info, an intermediate representation (RouteInfo) is created. This is done on the GetRouteInfo method, which contains some interesting stuff, highlighted bellow:

private static RouteInfo GetRouteInfo(
IInlineConstraintResolver constraintResolver,
Dictionary<string, RouteTemplate> templateCache,
ActionDescriptor action)
{
// ...

var routeInfo = new RouteInfo()
{
ActionDescriptor = action,
RouteTemplate = action.AttributeRouteInfo.Template,
// ...
};


RouteTemplate parsedTemplate;
if (!templateCache.TryGetValue(action.AttributeRouteInfo.Template, out parsedTemplate))
{
parsedTemplate = TemplateParser.Parse(action.AttributeRouteInfo.Template); // (1)
templateCache.Add(action.AttributeRouteInfo.Template, parsedTemplate);
}

routeInfo.ParsedTemplate = parsedTemplate;
routeInfo.Name = action.AttributeRouteInfo.Name;
// ...

routeInfo.Defaults = routeInfo.ParsedTemplate.Parameters // (2)
.Where(p => p.DefaultValue != null)
.ToDictionary(p => p.Name, p => p.DefaultValue, StringComparer.OrdinalIgnoreCase);

return routeInfo;
}

As highlighted above this is where the route template is actually parsed (1) and where the default route parameter values are prepared (2). The data structures mentioned on steps 2 and 3 are created from this info. Actually, for URL matching entries the parsed route template is given to a TemplateMatcher which be using when matching an URL. It’s also worth mentioning that all the URL matching entries are associated to a target IRouter which by default is MvcRouteHandler, already mentioned. This means that when one of this entries matches, it computes the route values as appropriate and delegates execution to its target.

On the last step, InnerAttributeRoute is created with all the gathered info. Since it is an IRouter, we should take a look at its RouteAsync method:

public async Task RouteAsync([NotNull] RouteContext context)
{
foreach(var matchingEntry in _matchingEntries)
{
var requestPath = context.HttpContext.Request.Path.Value;

var values = matchingEntry.TemplateMatcher.Match(requestPath); // (1)
if (values == null)
{
// If we got back a null value set, that means the URI did not match
continue;
}

var oldRouteData = context.RouteData;

var newRouteData = new RouteData(oldRouteData);
newRouteData.Routers.Add(matchingEntry.Target);
MergeValues(newRouteData.Values, values); // (2)


if (!RouteConstraintMatcher.Match( // (3)
matchingEntry.Constraints,
newRouteData.Values,
// ...))
{
continue;
}


context.RouteData = newRouteData;
await matchingEntry.Target.RouteAsync(context); // (4)

// ...

if (context.IsHandled) // (5)
{
break;
}
}
}

As expected, the URL matching entries are iterated and each entry’s template matcher is invoked (1). If the template matches, the current route values are merged with the ones extracted from the template (2). Next, the route constraints are checked (3) and if they match the target IRouter is invoked (4). An important aspect is that if the target doesn’t handle the request (5), other URL matching entries and eventually other routers will be attempted. This is different from previous versions of MVC, on which we’d get an HTTP not found.

As a side note: this method has a lot in common with the corresponding method on TemplateRoute (covered on the previous post). Both use template and constraint matchers and delegate on a target router.

Note that at (4) the route data contains important tokens extracted from the route template and/or defaults and/or constraints, such as “action” and “controller”. These route values are used by MvcRouteHandler to actually select an action.

public async Task RouteAsync([NotNull] RouteContext context)
{
// ...
var actionSelector = services.GetRequiredService<IActionSelector>();
var actionDescriptor = await actionSelector.SelectAsync(context);

if (actionDescriptor == null)
{
return;
}

// ...

await InvokeActionAsync(context, actionDescriptor);
context.IsHandled = true;

// ...
}

This might seem like we’re taking a step back. We had a template that was originally extracted from an action descriptor and now we’re going to select an action again? Keep in mind that applications can also define convention-based routes, where a single template may match multiple controllers/actions. All the framework knows at this point is that a route template matched, but it still needs to determine if the corresponding action exists, if action constraints match (e.g. HTTP method) and so on. All this common logic is triggered at this point.

An that’s it for MVC routing. This has been a long post, but hopefully it can provide some guidance when following ASP.NET source code.

ASP.NET 5 Routing – Part I

In this series of two posts I’ll cover routing on ASP.NET 5, starting from the core components and then going into how MVC fits on the routing system.


EDIT 07/2016: while most off this post still applies to the release of ASP.NET Core 1.0, there were some changes to keep in mind while reading it:

  • AspNet -> AspNetCore namespace renames.
  • The TemplateRoute class is now Route.
  • An IRouter that can handle a request now sets the resulting RequestDelegate on RouteContext instead of setting the IsHandled flag as mentioned below. The invocation of the request delegate is now centralized on the RouterMiddleware.

On previous versions of ASP.NET, routing started as a feature of MVC and was later extracted to a dedicated assembly (System.Web.Routing) and integrated on .NET. When Web API was released it brought the need of hosting outside of IIS, which required a dedicated routing system (actually a facade that could delegate on ASP.NET routing when hosting on IIS).

Similar to how MVC and Web API are unified, there’s now a single unified routing system, which includes features previously found on both routing systems and an improved syntax for both convention- and attribute-based routes; constraints, optional parameters and default values can all be specified inline on the route template. In addition, a route handler can now return control to the routing system indicating that it didn’t handle the request, allowing the following routes/handlers to be attempted. This didn’t happen on previous versions: if, for instance, an MVC route template was matched but couldn’t actually be resolved (e.g. no controller with the attempted name) an HTTP not found was immediately returned.

As you might expect by now, ASP.NET 5 routing is implemented as a middleware that can be included on the request pipeline: the RouterMiddleware. To include it on the pipeline one can use the UseRouter extension method depicted below:

public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, IRouter router)
{
  return builder.UseMiddleware<RouterMiddleware>(router);
}

This method takes an IRouter, whose definition is similar to the request delegate, since it takes a context and returns a task that represents the request completion:

public interface IRouter
{
   Task RouteAsync(RouteContext context);
   // ...
}

However, the RouteContext includes a bit more information, namely the RouteData and the IsHandled flag. The RouteData contains things such as the values extracted from the route template and the available routers; the IsHandled flag is used by the routers to signal if they were able to handle a request.

public class RouteContext
{
   public HttpContext HttpContext { get; private set; }
   public bool IsHandled { get; set; }
   public RouteData RouteData { ... }
}

These are the very basic elements of the routing system, which allow to build the RouterMiddleware:

public class RouterMiddleware
{
  private readonly ILogger _logger;
  private readonly RequestDelegate _next;
  private readonly IRouter _router;

  // ...

  public async Task Invoke(HttpContext httpContext)
  {
    var context = new RouteContext(httpContext);
    context.RouteData.Routers.Add(_router);

    await _router.RouteAsync(context);

    if (!context.IsHandled)
    {
      _logger.LogVerbose("Request did not match any routes.");
      await _next.Invoke(httpContext);
    }
}

ASP.NET includes some implementations of IRouter that are the base for application routes. One of them is TemplateRoute, which pairs a route template with another IRouter (the target); if the route template matches the current request, the route values are extracted from the template parameters and the request is forwarded to the target router.

public class TemplateRoute : INamedRouter
{
  public TemplateRoute(
     IRouter target,
     string routeTemplate,
     IInlineConstraintResolver inlineConstraintResolver)
  // ...
}

To illustrate the direct usage of this class, lets first define a custom hello world IRouter that extracts the name to greet from a route value named “name”.

public class SayHelloFromRouteDataRouter : IRouter
{
    public async Task RouteAsync(RouteContext context)
    {
        object name;
        if(context.RouteData.Values.TryGetValue("name", out name))
        {
            await context.HttpContext.Response.WriteAsync(String.Format("Hello {0} from ASP.NET 5 world!", name));
            context.IsHandled = true;
        }            
    }

    // ...
}

If the name route value is present, the handler writes the response and marks the route context as handled; otherwise, it returns control to the parent router for further processing. We can then define a route template and include our handler on the pipeline by combining it with a TemplateRoute on the Startup class:

app.UseRouter(
    new TemplateRoute(
        new SayHelloFromRouteDataRouter(),
        "hello/{name:alpha}",
        app.ApplicationServices.GetService<IInlineConstraintResolver>()
    )
);

Note the usage of an inline constraint on the route parameter. If we run the app we can see the expected output on the browser.

image

If we use an URL that doesn’t match the route (e.g. use only numbers on the name segment) we’ll get an HTTP not found or the result of another middleware on the pipeline that handles the request.

Another built in router is RouteCollection, which takes an ordered list of routers and returns when one of them handles the request. The following code excerpt illustrates the usage of a route collection; I also added a default value for the name parameter as an example of default values.

var routes = new RouteCollection();
routes.Add(new TemplateRoute(
        new SayHelloFromRouteDataRouter(),
        "hello/{name:alpha=JohnDoe}",
        app.ApplicationServices.GetService<IInlineConstraintResolver>()
    ));
routes.Add(new TemplateRoute(
        new SayGoodbyeRouter(),
        "bye",
        null
    ));
app.UseRouter(routes);

And that’s it for this part. On the following post I’ll detail how MVC leverages the routing building blocks to route requests to controller/actions.