The first article appeared here in December 2003, and just kept coming.
Quand on n'avance pas, on recule.
David Starr and I are putting together a Development Practices and Craftsmanship track for the Agile 2014 conference. The conference is in Orlando next summer (July 28 – August 1).
From David’s post on the subject:
Both Scott and I want to bring this back to roots: Code. Software. Technical practices. You know, the stuff everyone wants to write index cards about. This track is for you, not your boss or your PM or your Scrum Master or anyone who doesn’t laugh at a good PInvoke() joke. And I want to hear from the awesome Elegant Code bloggers, commenters, and readers. You guys have amazing ideas. Let’s hear them.
This release of Visual Studio 2013 and ASP.NET features YAMF - yet another membership framework. The new features build on top of an assembly and NuGet package named Microsoft.AspNet.Identity.Core.
The core identity abstractions are interface based and revolve around the concepts of users and stores. Users are the people who are happy to authenticate themselves to your web application, while stores are the components happy to persist and retrieve user information from a data source.
These abstractions are more flexible and composable than the membership providers from previous releases of ASP.NET and don’t rely on the “membership provider” base classes that have been around since ASP.NET 2.0.
In the next few blog posts we’ll look at how the components work, how to customize the identity features, how to work with 3rd party claims, and how to build a custom store using a non-relational database (specifically MongoDB).
This first post in the series looks at the core abstractions. It’s important to note that the Identity Core assembly defines mostly abstractions with no implementations. A later post will look at components provided by Microsoft to implement these interfaces and provide membership features backed by SQL Server.
A user object must implement the IUser interface, which requires every user to have at least an ID and a name. The most notable aspect of IUser is the Id property of type string. Strings work well for GUIDs but can be a bit tricky when using integers for keys as is common with SQL Server. More details in a future post.
The I*Store interfaces in the Core Identity assembly are factored to offer various levels of functionality.
IUserStore defines the bare minimum functionality you’d want for users – create, retrieve, update, and delete (CRUD) functionality. If your website wants to allow users to create local accounts with passwords, you’ll want a component implementing IUserPasswordStore (which is an IUserStore). For third party logins (Twitter and Facebook, for example), add in IUserLoginStore, and for claim storage there is an IUserClaimStore. Also, not shown in the class diagram above, is a collection of interfaces for roles (IRole, IUserRoleStore, etc). Let the debate on roles versus claims begin.
One of the curious aspects of IUserStore is its generic constraint.
public interface IUserStore<TUser> : IDisposable where TUser : IUser { // ... }
The implication is that any user object you want to use with the Identity features will need to implement IUser, which requires a dependency on the core identity assembly. It’s a bit odd to place a constraint on an interface definition since an interface doesn’t provide an implementation, but presumably the constraint helps to enforce the concept of all users having username and IDs of type string. The method signatures for IUserStore make this assumption, as does the UserManager.
The UserManager is a concrete class and it is the UserManager that provides the domain logic for working with user information. The UserManager knows when to hash a password, when to validate a user, and how to manage claims.
There are a few extensibility points with the UserManager, for example, the manager has a number of properties you can use to set a custom user validator (any object implementing IIdentityValidator), a custom password validator, and a custom password hasher. The primary extensibility point is via the UserManager constructor, which allows you to pass in any object implementing IUserStore. If you want to use a UserManager to manage user information stored in SQL Server, we’ll look at pre-built classes to do this in a later post, but it is also easy to create an IUserStore to work with a document database or other forms of storage.
Remember an IUserStore doesn’t know how how to work with user passwords, only an IUserPasswordStore knows about passwords. The UserManager is aware of the different core interfaces and will try to work with the capabilities of the store object given in the constructor. For example, if you use FindByIdAsync the UserManager can use the user store to query for a user by ID, but if you invoke FindAsync (which takes a username and a password), and the underlying store doesn’t implement the IUserPassword store interface, the UserManager will be forced to throw an exception.
It’s tricky to use a single concrete class for everything from users to passwords to claims, and tradeoffs must be made. We’ll also look at some of the tradeoffs in upcoming posts.
Something you might have noticed in the class diagrams is that every nearly every method on the store and manager classes is an async method. The return types for these methods are all Task<T>, where the Task represents the outstanding work of querying or updating user information.
These async methods work well with the Entity Framework version 6, which now includes an async API. We’ll also look at implementing these methods using a non-async data source in a future post.
The new Identity features are a welcome break from the dated model of the ASP.NET membership providers and more flexible than the the SimpleMembershipProvider in the last ASP.NET release.
Some developers will notice a lack of features compared to the membership providers of old (like the ability to track the last login and bad password attempts). Some of these features are easy to add, but it will be interesting to see the challenges that result if Microsoft attempts to add these features to the Identity bits in a future release (expect more interface definitions to appear).
Something I learned when building Memflex is that a membership framework has to make some assumptions and build itself around some core constraints which can limit the usefulness of a membership component in some applications – the needs for membership are just so varied in this big world. Still, the Identity bits have done a good job of abstracting away persistence concerns from the logic of managing users, logins, clams, and passwords, and as we’ll see in a future post (lots of future promises in this post), the implementation of the persistence APIs is mostly formulaic.
Sometimes I’m not interested in what the OWIN pipeline is doing as a whole, but I’m trying to figure out what each piece of middleware is doing in the pipeline configured by someone else. This is useful, for example, when debugging which piece of middleware is grabbing a request and short-circuiting the pipeline by sending an immediate response.
What the following IAppBuilder can do is wrap a real IAppBuilder and add a middleware wrapper for every piece of middleware added to the real IAppBuilder.
It’s middleware-middleware mania!
public class WrappingAppBuilder<TWrapper> : IAppBuilder { private readonly IAppBuilder _inner; private readonly object _wrappingOptions; public WrappingAppBuilder(IAppBuilder inner, object wrappingOptions) { _inner = inner; _wrappingOptions = wrappingOptions; } public IAppBuilder Use(object middleware, params object[] args) { _inner.Use(typeof (TWrapper), _wrappingOptions, GetDescription(middleware)); return _inner.Use(middleware, args); } private string GetDescription(object middleware) { var type = middleware as Type ?? middleware.GetType(); return type.Name; } public object Build(Type returnType) { return _inner.Build(returnType); } public IAppBuilder New() { return _inner.New(); } public IDictionary<string, object> Properties { get { return _inner.Properties; } } }
The wrapper is specified as a generic type parameter. The following wrapper will allow an external observer to inspect the environment dictionary before it goes into the next piece of middleware, and then again during the return.
using AppFunc = Func<IDictionary<string, object>, Task>; public class WrappingLogger { private readonly AppFunc _next; private readonly WrappingOptions _options; private readonly string _middlewareDescription; public WrappingLogger( AppFunc next, WrappingOptions options, string middlewareDescription) { _next = next; _options = options; _middlewareDescription = middlewareDescription; } public async Task Invoke(IDictionary<string, object> environment) { _options.BeforeNext(_middlewareDescription, environment); await _next(environment); _options.AfterNext(_middlewareDescription, environment); } }
The above class is using the following options.
using LogFunc = Action<object, IDictionary<string, object>>; public class WrappingOptions { public LogFunc BeforeNext { get; set; } public LogFunc AfterNext { get; set; } }
And configuration is as easy as:
public void Configuration(IAppBuilder app) { var options = new WrappingOptions() { BeforeNext = (middleware, environment) => Debug.WriteLine("Calling into: " + middleware), AfterNext = (middleware, environment) => Debug.WriteLine("Coming back from: " + middleware), }; app = new WrappingAppBuilder<WrappingLogger>(app, options); // ... // the rest of the middleware }
The above options provide some very crude request logging, but with a little work you can watch for dictionary changes and add timing information.
Coming up soon: the new identity bits.
Previous posts in this series.
Perhaps all you want to do with middleware is perform some simple logging for an HTTP request and response.
Here’s a class that can hold the options for which keys the application needs to log, and and an Action to encapsulate the logging logic.
public class SimpleLoggerOptions { public IList<string> RequestKeys { get; set; } public IList<string> ResponseKeys { get; set; } public Action<string, object> Log { get; set; } }
The logging middleware itself just needs to invoke the Action delegate at the appropriate time, and with the appropriate values, all the while letting each request pass through into the rest of the pipeline by calling into the next AppFunc.
using AppFunc = Func<IDictionary<string, object>, Task>; public class SimpleLogger { private readonly AppFunc _next; private readonly SimpleLoggerOptions _options; public SimpleLogger(AppFunc next, SimpleLoggerOptions options) { _next = next; _options = options; } public async Task Invoke(IDictionary<string, object> environment) { foreach (var key in _options.RequestKeys) { _options.Log(key, environment[key]); } await _next(environment); foreach (var key in _options.ResponseKeys) { _options.Log(key, environment[key]); } } }
Of course there could be some additional code inside the middleware to validate parameters and make sure a key exists before indexing into the environment dictionary, but given a simple extension method to help with registration …...
public static class AppBuilderExtensions { public static IAppBuilder UseSimpleLogger( this IAppBuilder app, SimpleLoggerOptions options) { return app.Use<SimpleLogger>(options); } }
… it is now easy to add logging to any application by registering the simple logger as the first piece of middleware. The keys specified below are all keys required by the OWIN specification.
public class Startup { public void Configuration(IAppBuilder app) { var options = new SimpleLoggerOptions { Log = (key, value) => Debug.WriteLine("{0}:{1}", key, value), RequestKeys = new[] {"owin.RequestPath", "owin.RequestMethod"}, ResponseKeys = new[] {"owin.ResponseStatusCode"} }; app.UseSimpleLogger(options); // ... and more ... } }
Next up: how to shim and log every other piece of middleware.
Previous posts in this series.
Months ago I kicked off a series a posts about working with Katana and OWIN.
In those posts I mostly used helper methods provided by Katana packages to add functionality to the OWIN pipeline using simple Funcs.
public class Startup { public void Configuration(IAppBuilder app) { app.UseHandlerAsync((request, response) => { res.ContentType = "text/plain"; return res.WriteAsync("Hello, World!"); }); } }
The helper method here is UseHandlerAsync, which is an extension method from Owin.Extensions that allows me to register a delegate that takes an OwinRequest and an OwinResponse and returns a Task. Behind the scenes, UseHandlerAsync transforms the OWIN environment of IDictionary<string, object> for each request into parameters that are easier to use (OwinRequest and OwinResponse) then calls into my code. The end result is a pipeline that responds to every request with “Hello!”.
You can work with Katana at a lower level and for more complex middleware, you typically will need more than just a delegate.
The basic pattern followed by most middleware components is to first provide an options object that someone can use to configure the middleware and customize the behavior for a specific application.
For example, let’s imagine we still want middleware to write a greeting like “Hello, World!”, but provide options to change the text and also include a timestamp.
public class HelloWorldOptions { public HelloWorldOptions() { IncludeTimestamp = true; Name = "World"; } public bool IncludeTimestamp { get; set; } public string Name { get; set; } }
Now we can write a full middleware component that uses these options and the basic OWIN environment to respond to requests.
public class HelloWorldComponent { readonly AppFunc _next; readonly HelloWorldOptions _options; public HelloWorldComponent(AppFunc next, HelloWorldOptions options) { _next = next; _options = options; } public async Task Invoke(IDictionary<string, object> environment) { var response = environment["owin.ResponseBody"] as Stream; using (var writer = new StreamWriter(response)) { if (_options.IncludeTimestamp) { await writer.WriteAsync(DateTime.Now.ToLongTimeString()); } await writer.WriteAsync("Hello, " + _options.Name + "!"); } } }
The class needs to have a constructor that takes an AppFunc and an options object. The AppFunc represents the next middleware component in the pipeline, and we’ll see an example in a later post where we will use the next AppFunc, but here we are ignoring any other middleware and taking control of every response.
The class also needs an Invoke method that matches the AppFunc delegate (Func<IDictionary<string, object>, Task>).
Finally, most middleware will provide extension methods for IAppBuilder that make it easy to configure themselves into the pipeline.
public static class AppBuilderExtensions { public static void UseHelloWorld( this IAppBuilder app, HelloWorldOptions options = null) { options = options ?? new HelloWorldOptions(); app.Use<HelloWorldComponent>(options); } }
Now spinning up a web service that will greet the earth looks like this:
public class Startup { public void Configuration(IAppBuilder app) { app.UseHelloWorld(new HelloWorldOptions { IncludeTimestamp = false, Name = "Earth" }); } }
The above code represents the same functionality we had before, but with a lot more flexibility.
Coming up next: logging middleware.
My new C# Generics course on Pluralsight includes topics for everyone.
For beginners:
- Why generic types are useful.
- A demonstration of all the concrete collection types in System.Collections.Generic
- How to build basic generic types (generic classes, generic interfaces, and generic delegates).
For intermediates:
- How to apply generic constraints (including what you can and can’t do with constraints).
- How to cleanup generic code (remove those ugly type parameters from business logic).
- How to use built-in generic delegates like Func, Action, Predicate, Converter, and EventHandler.
On the advanced side:
- How to refactor covariant and contravariant interfaces out of an invariant generic IRepository interface that works against the Entity Framework.
- How to use reflection to discover generic parameters and generic type definitions, as well as build generic types and invoke generic methods in late bound fashion.
- How to build an IoC container with a fluent-ish API that supports nested dependencies and unbound generics.