Page 50 - MSDN Magazine, June 2019
P. 50
Cutting EdgE DINO ESPOSITO Revisiting the ASP.NET Core Pipeline
Nearly any server-side processing environment has its own pipeline of pass-through components to inspect, re-route or modify incom- ing requests and the outgoing responses. Classic ASP.NET had it arranged around the idea of HTTP modules, whereas ASP.NET Core employs the more modern architecture based on middleware components. At the end of the day, the purpose is the same—letting configurable external modules intervene in the way the request (and later the response) goes through in the server environment. The primary purpose of middleware components is altering, and filter- ing in some way, the flow of data (and in some specific cases just short-circuiting the request, stopping any further processing).
The ASP.NET Core pipeline is nearly unchanged since version 1.0oftheframework,buttheupcomingreleaseofASP.NETCore 3.0 invites a few remarks on the current architecture that have gone for the most part unnoticed. So, in this article, I’ll revisit the overall functioning of the ASP.NET Core runtime pipeline and focus on the role and possible implementation of HTTP endpoints.
ASP.NET Core for the Web Back End
Especially in the past couple of years, building Web applications with the front end and back end completely decoupled has become quitecommon.Therefore,mostASP.NETCoreprojectsaretoday simple Web API, UI-less projects that just provide an HTTP façade to a single-page and/or mobile application built, for the most part, with Angular, React, Vue and their mobile counterparts.
When you realize this, a question pops up: In an application that’s not using any Razor facilities, does it still make sense to bind to the MVC application model? The MVC model doesn’t come for free, and in fact, to some extent, it may not even be the most lightweight option once you stop using controllers to serve action results. To press the question even further: Is the action result concept itself strictly necessary if a significant share of the ASP.NET Core code is written just to return JSON payloads?
With these thoughts in mind, let’s review the ASP.NET Core pipeline and the internal structure of middleware components and the list of built-in runtime services you can bind to during startup.
The Startup Class
In any ASP.NET Core application, one class is designated as the application bootstrapper. Most of the time, this class takes the name of Startup. The class is declared as a startup class in the configura- tion of the Web host and the Web host instantiates and invokes it via reflection. The class can have two methods—ConfigureServices (optional) and Configure. In the first method, you receive the
current (default) list of runtime services and are expected to add more services to prepare the ground for the actual application logic. In the Configure method, you configure for both the default services and for those you explicitly requested to support your application.
The Configure method receives at least an instance of the appli- cation builder class. You can see this instance as a working instance of the ASP.NET runtime pipeline passed to your code to be config- ured as appropriate. Once the Configure method returns, the pipeline workflow is fully configured and will be used to carry on any further request sent from connected clients. Figure 1 provides a sample implementation of the Configure method of a Startup class.
The Use extension method is the principal method you employ to add middleware code to the otherwise empty pipeline work- flow. Note that the more middleware you add, the more work the server needs to do to serve any incoming requests. The most minimal is the pipeline, the fastest will be the time-to-first-byte (TTFB) for the client.
Youcanaddmiddlewarecodetothepipelineusingeitherlamb- das or ad hoc middleware classes. The choice is up to you: The lambda is more direct, but the class (and preferably some extension methods) will make the whole thing easier to read and maintain. The middleware code gets the HTTP context of the request and a reference to the next middleware in the pipeline, if any. Figure 2 presents an overall view of how the various middleware compo- nents link together.
Each middleware component is given a double chance to inter- vene in the life of the ongoing request. It can pre-process the request as received from the chain of components registered to run earlier,
Figure 1 Basic Example of the Configure Method in the Startup Class
public void Configure(IApplicationBuilder app) {
app.Use(async (context, nextMiddleware) => {
await context.Response.WriteAsync("BEFORE"); await nextMiddleware();
await context.Response.WriteAsync("AFTER");
});
app.Run(async (context) => {
var obj = new SomeWork(); await context
.Response
.WriteAsync("<h1 style='color:red;'>" +
}); }
obj.SomeMethod() + "</h1>");
46 msdn magazine