Back in the day we would write Http Handlers to process requests that needed to be tied to a particular endpoint while avoiding the overhead of the Web Forms page life cycle. This is an older concept but you can find plenty of examples in the DasBlog code line. This was before RESTful APIs were popular, even preceding the first versions of WCF. HttpHandlers were a straightforward way to provide an RPC like interface, you could even define HTTP verbs in the web.config:

<add verb="GET" path="DeleteItem.aspx" type="newtelligence.DasBlog.Web.Services.DeleteItemHandler, newtelligence.DasBlog.Web.Services"/> 

Your handler class (DeleteItemHandler in this example) would need to inherit from IHttpHandler. The web request and response are handled in the ProcessRequest(HttpContext). Here is an abbreviated version of a handler over at DasBlog:

public class DeleteItemHandler : IHttpHandler 
{
     public bool IsReusable
     {
         get { return true; }
     }

    public void ProcessRequest( HttpContext context )
     {
         // All the work is done here…
     }
}

This solution provided an incredibly versatile way of defining completely custom pipelines and routes akin to ISAPI extension but with much less C++.

Middleware

Middleware, like HttpHandlers, are assembled into the application pipeline to handle both requests and responses of ASP.NET Core applications. The important improvements over HttpHandlers is that each Middleware component gets to decide whether to pass the request along and you also determine explicitly where that component is order in the pipeline.

Here is the basic skeleton of a Middleware, where Invoke takes the place ProcessRequest:

public class DeleteItemMiddleware
{
     public DeleteItemMiddleware(RequestDelegate next)
     {
         // This is an HTTP Handler replacement so we do nothing...
     }

    public async Task Invoke(HttpContext context)
     {
         // All the work is now done here…
     }
}

Note the constructor signature is always necessary but as this is a HttpHandler replacement (representing the very end of the pipeline) we do nothing with it, I actually wonder why middleware is not defined through an interface by design.

The following extension method exposes the middleware to your IApplicationBuilder:

public static IApplicationBuilder AddDeleteItem(this IApplicationBuilder builder)
{
     return builder.UseMiddleware<DeleteItemMiddleware>();
}

The final step is defining what URI the Middleware will respond to, for HttpHandlers this would have been defined in the web.config path property. For Middleware this is defined programmatically in Startup.cs as follows:

app.MapWhen(
     context => context.Request.Path.ToString().EndsWith("/deleteitem"),
     appBranch => {
         appBranch.AddDeleteItem();
     });