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!

Advertisement

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!

OAuth 2.0 library for .NET native apps

A few months ago I wrote about OAuth 2.0 for native apps, mentioning App Auth, an OAuth 2.0 client library for native Android and iOS applications. Recently I discovered that a similar library has been release for .NET! It is a PCL (Xamarin included) and it features the same security concerns as App Auth, following the IETF recommendations for native OAuth 2.0 clients. In addition, it also supports OpenId Connect features, such as validating identity  tokens. Very nice!