Previous versions of ASP.NET had a very close relationship with Internet Information Services (IIS), Microsoft's flagship web server that ships with Windows. In fact, IIS was the only supported way to host ASP.NET.
Wanting to change this, Microsoft defined the Open Web Interface for .NET (OWIN) specification, which you can read about at http://owin.org. In a nutshell, it is the standard for decoupling server and application code, and for the execution pipeline for web requests. Because it is just a standard and knows nothing about the web server (if any), it can be used to extract its features.
.NET Core borrowed heavily from the OWIN specification. There are no more Global.asax, web.config, or machine.config configuration files, modules, or handlers. What we have is the following:
The bootstrap code in Program.Main declares a class that contains a convention-defined method (Startup will be used if no class is declared).
This conventional method, which should be called Configure, receives a reference to an IApplicationBuilder instance (it can take other services to be injected from the service provider).
You then start adding middleware to the IApplicationBuilder; this middleware is what will handle your web requests.
A simple example is in order. First, the bootstrap class, which is by default named Program:
public class Program
{
public static void Main(string [] args) => CreateWebHostBuilder(args).Build().Run();
Things can get more complicated, but don't worry too much about it now. Later on, I will explain what this all means. For the time being, it's enough to know that we are leveraging a Host to host Kestrel (the default host), and passing a conventional class called Startup. This Startup class looks like this (in a simplified way):
public class Startup
{ public IConfiguration Configuration { get; }
There are a couple of things here that deserve an explanation. First, you will notice that the Startup class does not implement any interface or inherit from an explicit base class. This is because the Configure method does not have a predefined signature, other than its name, taking as its first parameter an IApplicationBuilder. For example, the following is also allowed:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { ... }
This version even gives you more than what you asked for. But I digress. The IApplicationBuilder interface defines a Run method. This method takes a RequestDelegate parameter, which is a delegate definition that accepts an HttpContext (remember that?) as its sole parameter and returns a Task. In my example, we made it asynchronous by adding async and await keywords to it, but it need not be so. All you have to do is make sure you extract whatever you want from the HttpContext and write whatever you want to it—this is your web pipeline. It wraps both the HTTP request and response objects, and we call it middleware.
The Run method is a full-blown pipeline on its own, but we can plug other steps (middleware) into the pipeline by using the (pun intended) Use method:
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello from a middleware!");
await next();
});
This way, we can add multiple steps, and they all will be executed in the order they were defined:
Because the first step was added before the second, it wraps it, so any exceptions thrown by step two will be caught by step one; if they were added in a different order, this wouldn't happen.
The Use method takes anHttpContextinstance as its parameter and returns aFunc<Task>, which is normally a call to the next handler, so that the pipeline proceeds.
We could extract the lambda to its own method, like this:
It is even possible to extract the middleware to its own class and apply it using the generic UseMiddleware method:
public class Middleware
{
private readonly RequestDelegate _next;
public Middleware(RequestDelegate next)
{
this._next = next;
}
public async Task InvokeAsync(HttpContext context)
{
await context.Response.WriteAsync("This is a middleware class!");
}
}
//in Startup.Configure app.UseMiddleWare<Middleware>();
In this case, the constructor needs to take as its first parameter a pointer to the next middleware in the pipeline, as a RequestDelegate instance.
I think by now you've got the picture: OWIN defines a pipeline to which you can add handlers which are then called in sequence. The difference between Run and Use is that the former ends the pipeline—that is, it won't call anything after itself.
The following diagram (from Microsoft) clearly shows this:
Image taken from https://docs.microsoft.com/en-us/dotnet/architecture/blazor-for-web-forms-developers/middleware
The first middleware, in a way, wraps all of the next ones. For example, imagine that you want to add exception handling to all the steps in the pipeline. You could do something like this:
app.Use(async (context, next) =>
{
try
{
//log call
await next(context);
}
catch (Exception ex)
{
//do something with the exception
} await context.Response.WriteAsync("outside an exception handler");
});
The call to next() is wrapped in a try...catch block, so any exception that may be thrown by another middleware in the pipeline, as long as it was added after this one, will be caught.
You can set the status code of a response, but be aware that, if an exception is thrown, it will be reset to 500Server Error!
Why is OWIN important? Well, because ASP.NET Core (and its MVC implementation) are built on it. We will see later that in order to have an MVC application, we need to add the MVC middleware to the OWIN pipeline in the Startup class's Configure method, normally as shown in the following code, using the new endpoint routing and the default route:
{ endpoints.MapDefaultControllerRoute(); });
As you know, this book talks essentially about the MVC pattern, but we could go equally with this kind of middleware, without any MVC stuff; it's just that it would be much harder to tackle complexity, and MVC does a very good job of that.
OWIN is essentially ASP.NET Core middleware. Everything that we add in a UseXXX extension is middleware. Let's look at how we can host an ASP.NET Core project next.