Page 51 - MSDN Magazine, June 2019
P. 51

Middleware #1
Middleware #1
Middleware #1
The lambda receives an Http- Context object and does whatever it’s supposed to do in the context of the application.
A middleware component that deliberately doesn’t yield to the next actually terminates the chain, causing the response to be sent to the requesting client. A good example of this is the UseStaticFiles middleware, which serves a static resource under the specified Web
root folder and terminates the request. Another example is Use- Rewriter, which may be able to order a client redirect to a new URL. Without a terminating middleware, a request can hardly result in some visible output on the client, though a response is still sent as modified by running middleware, for example through the addi- tion of HTTP headers or response cookies.
There are two dedicated middleware tools that can also be used to short circuit the request: app.Map and app.MapWhen. The for- mer checks if the request path matches the argument and runs its
own terminating middleware, as shown here:
app.Map("/now", now => {
now.Run(async context => {
var time = DateTime.UtcNow.ToString("HH:mm:ss"); await context
.Response
.WriteAsync(time); });
});
The latter, instead, runs its own terminating middleware only if a specified Boolean condition is verified. The Boolean condition results from the evaluation of a function that accepts an Http- Context. The code in Figure 3 presents a very thin and minimal Web API that only serves a single endpoint and does that without anything like a controller class.
If the URL matches /country, the terminating middleware reads a parameter from the query string and arranges a call to the repos- itory to get the list of matching countries. The list object is then manually serialized in a JSON format directly to the output stream. By just adding a few other map routes you could even extend your Web API. It can hardly be simpler than that.
What About MVC?
In ASP.NET Core, the whole MVC machinery is offered as a black-box runtime service. All you do is bind to the service in the Configure- Services method and configure its routes in the Configure method,
as shown in the code here:
public void ConfigureServices(IServiceCollection services) {
// Bind to the MVC machinery
services.AddMvc(); }
public void Configure(IApplicationBuilder app) {
// Use the MVC machinery with default route app.UseMvcWithDefaultRoute();
// (As an alternative) Use the MVC machinery with attribute routing
app.UseMvc(); }
Pre-Processing Code
Await
Post-Processing Code
Pre-Processing Code Pre-Processing Code
Post-Processing Code Post-Processing Code
Await
Await
Terminating Middleware
Figure 2 The ASP.NET Core Runtime Pipeline
and then it’s expected to yield to the next component in the chain. When the last component in the chain gets its chance to pre-process the request, the request is passed to the terminating middleware for the actual processing aimed at producing a concrete output. After that, the chain of components is walked back in the reverse order as shown in Figure 2, and each middleware has its second chance to process—this time, though, it will be a post-processing action. In the code of Figure 1, the separation between pre- processing and post-processing code is the line:
await nextMiddleware();
A middleware component that deliberately doesn’t yield to the next actually terminates the chain, causing the response to be sent to the requesting client.
The Terminating Middleware
Key in the architecture shown in Figure 2 is the role of the terminating middleware, which is the code at the bottom of the Configure method that terminates the chain and processes the request. All demo ASP.NET Core applications have a terminating lambda, like so:
app.Run(async (context) => { ... };
Figure 3 A Very Minimal ASP.NET Core Web API
public void Configure(IApplicationBuilder app, ICountryRepository country)
{
app.Map("/country", countryApp => {
countryApp.Run(async (context) => {
var query = context.Request.Query["q"];
var list = country.AllBy(query).ToList(); var json = JsonConvert.SerializeObject(list); await context.Response.WriteAsync(json);
}); });
// Work as a catch-all app.Run(async (context) => {
await context.Response.WriteAsync("Invalid call"); }
});
msdnmagazine.com
June 2019 47


































































































   49   50   51   52   53