ASP.NET Core (1) - Journey of a Request

13 May 2021 , 3240 words

Typically, a request’s first stop at the server is a reverse-proxy, such as Apache and Nginx. The proxy works as a gateway, and send the appropriate request to the real server.

The default server is a fast, lightweight web server that Microsoft creates named Kestrel. Kestrel sees the http request, create an HttpContext object to hold the request’s details, and pass the object to our actual program.

The most important concept in ASP.NET Core is middleware pipeline, since ASP.NET Core uses middleware pipeline to handle every incoming request and to send out response.

A middleware, in our case, is simply a class that works on the http request or response. It can modify request, pass the request to another middleware, generate response, modify response, pass the response to another middleware, and so on. When we put middlewares in order, we have a pipeline. In this pipeline, each middleware sees the change on the HttpContext made by the previous middlewares, and can decides what to do with the object itself.

The pipeline executes in a bi-directional manner: a pipeline consists of middleware A, B, C may (considering the cases where some middleware short-circuits the execution) execute in this order: A-B-C-B-A.

For examples, consider the three commonly used middleware: ExceptionHandlerMiddleware, EndpointMiddleware, and RoutingMiddleWare.

RoutingMiddleware sees an request (in the form HttpContext), decides which handler should execute to handle the request, and pass it on to the next middleware, say EndpointMiddleware, to actually execute it. Another example would be ExceptionHandlerMiddleware. This middleware catches the exception thrown by the application, modify the response as if it’s a new request to specific endpoint like “/ErrorPage”, pass it back to the pipeline to execute the request, so that user will see a nice error page made by the developer.

In this example, what should be the correct order to put RoutingMiddleWare, EndpointMiddleware, and ExceptionHandlerMiddleware? The answer is:

  1. ExceptionHandlerMiddleware
  2. RoutingMiddleWare
  3. EndpointMiddleware

When a request comes in, the ExceptionHandlerMiddleware will ignore it first, as it is designed to work on possible exceptions in response. The request then comes to RoutingMiddleWare, which decides what endpoint should be selected for a certain URL. Then, the EndpointMiddleware executes, which usually means a response is generated. The response will then go reverse order to RoutingMiddleWare, which sits idle at this time, as it does not care about response. Lastly, the response goes to ExceptionHandlerMiddleware, which will handle the possible exceptions thrown by RoutingMiddleWare and EndpointMiddleware.

It is worth noting that, some middleware such as ExceptionHandlerMiddleware or StatusCodePagesMiddleware would handle the response by re-executing the pipeline. For example, StatusCodePagesMiddleware sees a 404 response with no body or message, it re-execute the pipeline as if the request is for URL “/404”. Then the same old path is travelled as if it’s a new request. If anything goes right, the 404 handler will be reached and the custom page is rendered (with 404 being the status code, even though the resource is found the second time).