Anatomy of ASP.NET Core Requests (60m)

863d6186d3bc32b7c9036101c47d5d5b?s=47 Steve Gordon
June 04, 2020
540

Anatomy of ASP.NET Core Requests (60m)

Have you ever wondered how a browser/client request actually results in a response from ASP.NET Core? Have you ever been constrained by the default behaviour of ASP.NET Core and wanted to change how it works? If so, then this talk is for you.

In this session, Steve will explore the lifecycle of incoming requests in ASP.NET Core, touring through the layers from Kestrel, to hosting, to middleware and then on into MVC itself. You'll see how the pieces all fit together and learn about the places where you can inject your own implementations to customise, augment and override the ASP.NET Core defaults.

Steve will show the hidden extension points that can take you to power-user levels when building your ASP.NET Core applications, such as applying custom conventions, filters and model binding behaviour. You'll leave this talk with ideas for improving your ASP.NET Core applications and reducing code by refactoring cross-cutting concerns.

Don't fight the framework, customise it!

In this session, I provide an in-depth look at how ASP.NET Core processes HTTP requests. I'll describe the flow through the application host, the Kestrel web server, middleware and finally through MVC. The intent is to use this narrative to explain some advanced features of ASP.NET Core which support customising its behaviour.

Key take-aways:
- How application hosting works
- How Kestrel receives requests from the network Socket and parses them
- How the middleware pipeline can be used to respond to requests
- How endpoint routing works
- How filters can be used to intercept and modify requests within MVC
- How MVC finally handles a request.

This session is aimed at ASP.NET Core developers of intermediate/advanced experience.

863d6186d3bc32b7c9036101c47d5d5b?s=128

Steve Gordon

June 04, 2020
Tweet

Transcript

  1. Anatomy of ASP.NET Core Requests Steve Gordon @stevejgordon | stevejgordon.co.uk

    Resources: http://bit.ly/aspnetanatomy
  2. CLIENT HTTP REQUEST www.stevejgordon.co.uk/api/books

  3. Recursive Resolver Client Root-Level DNS Top Level Domain DNS (.uk)

    Second-Level DNS (.co.uk) queries refers refers IP Address Domain Name Server (stevejgordon.co.uk) refers
  4. Client (::64000) Web Server (101.101.121.64:443) ClientHello ServerHello Certificate ServerHelloDone ClientKeyExchange

    ChangeCipherSpec Finished ChangeCipherSpec Finished TCP TLS
  5. @stevejgordon www.stevejgordon.co.uk GET /api/books HTTP/1.1 Host: stevejgordon.co.uk Accept: application/json, text/plain,

    */* Accept-Language: en-GB Accept-Encoding: br, gzip, deflate User-Agent: ClientApplication/1.0 Correlation-Id: 48d3bbb5-643c-469c-899b-a547316f25b6 (blank line)
  6. @stevejgordon www.stevejgordon.co.uk GET /api/books HTTP/1.1 Host: stevejgordon.co.uk Accept: application/json, text/plain,

    */* Accept-Language: en-GB Accept-Encoding: br, gzip, deflate User-Agent: ClientApplication/1.0 Correlation-Id: 48d3bbb5-643c-469c-899b-a547316f25b6 (blank line)
  7. @stevejgordon www.stevejgordon.co.uk GET /api/books HTTP/1.1 Host: stevejgordon.co.uk Accept: application/json, text/plain,

    */* Accept-Language: en-GB Accept-Encoding: br, gzip, deflate User-Agent: ClientApplication/1.0 Correlation-Id: 48d3bbb5-643c-469c-899b-a547316f25b6 (blank line)
  8. @stevejgordon www.stevejgordon.co.uk GET /api/books HTTP/1.1 Host: stevejgordon.co.uk Accept: application/json, text/plain,

    */* Accept-Language: en-GB Accept-Encoding: br, gzip, deflate User-Agent: ClientApplication/1.0 Correlation-Id: 48d3bbb5-643c-469c-899b-a547316f25b6 (blank line)
  9. @stevejgordon www.stevejgordon.co.uk GET /api/books HTTP/1.1 Host: stevejgordon.co.uk Accept: application/json, text/plain,

    */* Accept-Language: en-GB Accept-Encoding: br, gzip, deflate User-Agent: ClientApplication/1.0 Correlation-Id: 48d3bbb5-643c-469c-899b-a547316f25b6 (blank line)
  10. KESTREL

  11. Client Internet KESTREL HTTP/S R e q u e s

    t R e s p o n s e Accept Connection ASP.NET Core Connection Middleware Listening on :80 and :443
  12. Kestrel Client Internet ASP.NET CORE {} Application Code HTTP/S HttpContext

    R e q u e s t R e s p o n s e Parse HTTP request
  13. Kestrel Client Internet ASP.NET CORE HTTP/S HttpContext Middleware Endpoint /

    MVC R e q u e s t R e s p o n s e
  14. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if

    (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
  15. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if

    (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
  16. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if

    (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
  17. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if

    (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
  18. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if

    (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
  19. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if

    (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
  20. @stevejgordon www.stevejgordon.co.uk R e q u e s t R

    e s p o n s e Endpoint Handles Request Pipeline Invoked HttpsRedirectionMiddleware EndpointRoutingMiddleware AuthenticationMiddleware AuthorizationMiddleware EndpointMiddleware DeveloperExceptionPageMiddleware ForwardedHeadersMiddleware HostFilteringMiddleware
  21. @stevejgordon www.stevejgordon.co.uk R e q u e s t R

    e s p o n s e Endpoint Handles Request Pipeline Invoked HttpsRedirectionMiddleware EndpointRoutingMiddleware AuthenticationMiddleware AuthorizationMiddleware EndpointMiddleware DeveloperExceptionPageMiddleware ForwardedHeadersMiddleware HostFilteringMiddleware
  22. @stevejgordon www.stevejgordon.co.uk R e q u e s t R

    e s p o n s e Endpoint Handles Request Pipeline Invoked HttpsRedirectionMiddleware EndpointRoutingMiddleware AuthenticationMiddleware AuthorizationMiddleware EndpointMiddleware DeveloperExceptionPageMiddleware ForwardedHeadersMiddleware HostFilteringMiddleware
  23. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.Use(async

    (ctx, next) => { var sw = Stopwatch.StartNew(); await next(); // call next middleware in pipeline sw.Stop(); var recorder = ctx.RequestServices.GetRequiredService<IMetricRecorder>(); recorder.RecordRequest(ctx.Response.StatusCode, sw.ElapsedMilliseconds); }); ... }
  24. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.Use(async

    (ctx, next) => { var sw = Stopwatch.StartNew(); await next(); // call next middleware in pipeline sw.Stop(); var recorder = ctx.RequestServices.GetRequiredService<IMetricRecorder>(); recorder.RecordRequest(ctx.Response.StatusCode, sw.ElapsedMilliseconds); }); ... }
  25. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.Use(async

    (ctx, next) => { var sw = Stopwatch.StartNew(); await next(); // call next middleware in pipeline sw.Stop(); var recorder = ctx.RequestServices.GetRequiredService<IMetricRecorder>(); recorder.RecordRequest(ctx.Response.StatusCode, sw.ElapsedMilliseconds); }); ... }
  26. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.Use(async

    (ctx, next) => { var sw = Stopwatch.StartNew(); await next(); // call next middleware in pipeline sw.Stop(); var recorder = ctx.RequestServices.GetRequiredService<IMetricRecorder>(); recorder.RecordRequest(ctx.Response.StatusCode, sw.ElapsedMilliseconds); }); ... }
  27. @stevejgordon www.stevejgordon.co.uk public class MetricMiddleware { private readonly RequestDelegate _next;

    private readonly IMetricRecorder _metricRecorder; public MetricMiddleware(RequestDelegate next, IMetricRecorder metricRecorder) { _next = next; _metricRecorder = metricRecorder; } public async Task InvokeAsync(HttpContext ctx) { var stopWatch = Stopwatch.StartNew(); await _next(ctx); stopWatch.Stop(); _metricRecorder.RecordRequest(ctx.Response.StatusCode, stopWatch.ElapsedMilliseconds); } }
  28. @stevejgordon www.stevejgordon.co.uk public class MetricMiddleware { private readonly RequestDelegate _next;

    private readonly IMetricRecorder _metricRecorder; public MetricMiddleware(RequestDelegate next, IMetricRecorder metricRecorder) { _next = next; _metricRecorder = metricRecorder; } public async Task InvokeAsync(HttpContext ctx) { var stopWatch = Stopwatch.StartNew(); await _next(ctx); stopWatch.Stop(); _metricRecorder.RecordRequest(ctx.Response.StatusCode, stopWatch.ElapsedMilliseconds); } }
  29. @stevejgordon www.stevejgordon.co.uk public class MetricMiddleware { private readonly RequestDelegate _next;

    private readonly IMetricRecorder _metricRecorder; public MetricMiddleware(RequestDelegate next, IMetricRecorder metricRecorder) { _next = next; _metricRecorder = metricRecorder; } public async Task InvokeAsync(HttpContext ctx) { var stopWatch = Stopwatch.StartNew(); await _next(ctx); stopWatch.Stop(); _metricRecorder.RecordRequest(ctx.Response.StatusCode, stopWatch.ElapsedMilliseconds); } }
  30. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseMiddleware<MetricMiddleware>();

    ... }
  31. @stevejgordon www.stevejgordon.co.uk public static class MetricMiddlewareExtensions { public static IApplicationBuilder

    UseMetrics(this IApplicationBuilder builder) { return builder.UseMiddleware<MetricMiddleware>(); } }
  32. @stevejgordon www.stevejgordon.co.uk public static class MetricMiddlewareExtensions { public static IApplicationBuilder

    UseMetrics(this IApplicationBuilder builder) { return builder.UseMiddleware<MetricMiddleware>(); } } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseMetrics(); ... }
  33. ENDPOINT ROUTING

  34. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if

    (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
  35. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if

    (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
  36. DEMO

  37. ASP.NET CORE MVC

  38. @stevejgordon www.stevejgordon.co.uk [ApiController] [Route("[controller]")] public class BooksController : ControllerBase {

    private readonly IBookRepository _bookRepository; public BooksController(IBookRepository bookRepository) { _bookRepository = bookRepository; } [HttpGet] public ActionResult<IEnumerable<BookOutputModel>> Get() { return Ok(_bookRepository.GetAll()); } ... }
  39. @stevejgordon www.stevejgordon.co.uk [ApiController] [Route("[controller]")] public class BooksController : ControllerBase {

    private readonly IBookRepository _bookRepository; public BooksController(IBookRepository bookRepository) { _bookRepository = bookRepository; } [HttpGet] public ActionResult<IEnumerable<BookOutputModel>> Get() { return Ok(_bookRepository.GetAll()); } ... }
  40. @stevejgordon www.stevejgordon.co.uk [ApiController] [Route("[controller]")] public class BooksController : ControllerBase {

    private readonly IBookRepository _bookRepository; public BooksController(IBookRepository bookRepository) { _bookRepository = bookRepository; } [HttpGet] public ActionResult<IEnumerable<BookOutputModel>> Get() { return Ok(_bookRepository.GetAll()); } ... }
  41. @stevejgordon www.stevejgordon.co.uk [ApiController] [Route("[controller]")] public class BooksController : ControllerBase {

    private readonly IBookRepository _bookRepository; public BooksController(IBookRepository bookRepository) { _bookRepository = bookRepository; } [HttpGet] public ActionResult<IEnumerable<BookOutputModel>> Get() { return Ok(_bookRepository.GetAll()); } ... }
  42. @stevejgordon www.stevejgordon.co.uk Controller Initialisation Action Execution View Rendering Result Execution

    Response View Result Data Result Controller Factory Controller Action Invoker
  43. None
  44. Authorization Filters R e q u e s t R

    e s p o n s e Middleware Forbidden void OnAuthorization(AuthorizationFilterContext ctx)
  45. DEMO

  46. Resource Filters Authorization Filters R e q u e s

    t R e s p o n s e Middleware Short-circuit void OnResourceExecuting(ResourceExecutingContext ctx); void OnResourceExecuted(ResourceExecutedContext ctx);
  47. DEMO

  48. Resource Filters Middleware Filters Authorization Filters R e q u

    e s t R e s p o n s e Middleware Short-circuit
  49. @stevejgordon www.stevejgordon.co.uk public class CompressionMiddlewarePipeline { public void Configure(IApplicationBuilder app)

    { app.UseResponseCompression(); } }
  50. @stevejgordon www.stevejgordon.co.uk public class CompressionMiddlewarePipeline { public void Configure(IApplicationBuilder app)

    { app.UseResponseCompression(); } } [ApiController] [Route("[controller]")] [MiddlewareFilter(typeof(CompressionMiddlewarePipeline))] public class AuthorsController : ControllerBase { // Action methods }
  51. @stevejgordon www.stevejgordon.co.uk public class CompressionMiddlewarePipeline { public void Configure(IApplicationBuilder app)

    { app.UseResponseCompression(); } } [ApiController] [Route("[controller]")] [MiddlewareFilter(typeof(CompressionMiddlewarePipeline))] public class AuthorsController : ControllerBase { // Action methods }
  52. Resource Filters Middleware Filters Controller Created Authorization Filters R e

    q u e s t R e s p o n s e Middleware
  53. CONTROLLER ACTION INVOKER

  54. Resource Filters Middleware Filters Controller Created Model Binding Authorization Filters

    R e q u e s t R e s p o n s e Middleware
  55. MODEL BINDERS

  56. @stevejgordon www.stevejgordon.co.uk [HttpGet] public ActionResult<IEnumerable<BookOutputModel>> Search(string keyword, int pageSize) {

    return Ok(_bookRepository.Search(keyword, pageSize)); } GET /books/search?keyword=performance?pageSize=10
  57. @stevejgordon www.stevejgordon.co.uk [HttpGet] public ActionResult<IEnumerable<BookOutputModel>> Search(string keyword, int pageSize) {

    return Ok(_bookRepository.Search(keyword, pageSize)); } GET /books/search?keyword=performance?pageSize=10
  58. @stevejgordon www.stevejgordon.co.uk [HttpGet] public ActionResult<IEnumerable<BookOutputModel>> Search(string keyword, int pageSize) {

    return Ok(_bookRepository.Search(keyword, pageSize)); } GET /books/search?keyword=performance?pageSize=10
  59. @stevejgordon www.stevejgordon.co.uk [HttpGet] public ActionResult<IEnumerable<BookOutputModel>> Search(string keyword, int pageSize) {

    return Ok(_bookRepository.Search(keyword, pageSize)); } GET /books/search?keyword=performance?pageSize=10
  60. @stevejgordon www.stevejgordon.co.uk [HttpGet] public ActionResult<IEnumerable<BookOutputModel>> Search(string keyword, int pageSize) {

    return Ok(_bookRepository.Search(keyword, pageSize)); } GET /books/search?keyword=performance?pageSize=10
  61. INPUT FORMATTERS

  62. DEMO

  63. Resource Filters Middleware Filters Controller Created Model Binding Authorization Filters

    Action Filters R e q u e s t R e s p o n s e Middleware Short-circuit void OnActionExecuting(ActionExecutingContext ctx);
  64. Resource Filters Middleware Filters Controller Created Model Binding Authorization Filters

    Action Filters Action Execution R e q u e s t R e s p o n s e Middleware void OnActionExecuted(ActionExecutedContext ctx);
  65. DEMO

  66. Resource Filters Middleware Filters Controller Created Model Binding Authorization Filters

    Action Filters Exception Filters Action Execution R e q u e s t R e s p o n s e Middleware void OnException(ExceptionContext context);
  67. DEMO

  68. Resource Filters Middleware Filters Controller Created Model Binding Authorization Filters

    Action Filters Result Filters Exception Filters Action Execution R e q u e s t R e s p o n s e Middleware void OnResultExecuting(ResultExecutingContext ctx); void OnResultExecuted(ResultExecutedContext ctx);
  69. Resource Filters Middleware Filters Controller Created Model Binding Authorization Filters

    Action Filters Result Filters Exception Filters Action Execution ActionResult Executed Output Formatter R e q u e s t R e s p o n s e Middleware
  70. Resource Filters Middleware Filters Controller Created Model Binding Authorization Filters

    Action Filters Result Filters Exception Filters Action Execution ActionResult Executed Resource Filters Middleware Filters Output Formatter R e q u e s t R e s p o n s e Middleware Result Filters
  71. DEMO

  72. Resource Filters Middleware Filters Controller Created Model Binding Authorization Filters

    Action Filters Result Filters Exception Filters Action Execution ActionResult Executed Resource Filters Middleware Filters Output Formatter R e q u e s t R e s p o n s e Middleware Result Filters
  73. @stevejgordon www.stevejgordon.co.uk R e q u e s t R

    e s p o n s e Endpoint Handles Request Pipeline Invoked HttpsRedirectionMiddleware EndpointRoutingMiddleware AuthenticationMiddleware AuthorizationMiddleware EndpointMiddleware DeveloperExceptionPageMiddleware ForwardedHeadersMiddleware HostFilteringMiddleware
  74. Kestrel Client Internet ASP.NET CORE HTTP/S HttpContext Middleware MVC R

    e q u e s t R e s p o n s e
  75. @stevejgordon www.stevejgordon.co.uk HTTP/1.1 200 OK Date: Sat, 23 May 2020

    07:20:56 GMT Content-Type: application/json; charset=utf-8 Server: Kestrel Transfer-Encoding: chunked Last-Modified: Tue, 31 Mar 2020 23:00:00 GMT [{"id":1,"title":"Pro .NET Memory Management","isbn":"978-1-4842-4026- 7","datePublished":"2018-11- 13T00:00:00","authors":[{"firstName":"Konrad","lastNam e":"Kokosa"}],"lastModified":"2020-04-01T00:00:00"}]
  76. @stevejgordon www.stevejgordon.co.uk HTTP/1.1 200 OK Date: Sat, 23 May 2020

    07:20:56 GMT Content-Type: application/json; charset=utf-8 Server: Kestrel Transfer-Encoding: chunked Last-Modified: Tue, 31 Mar 2020 23:00:00 GMT [{"id":1,"title":"Pro .NET Memory Management","isbn":"978-1-4842-4026- 7","datePublished":"2018-11- 13T00:00:00","authors":[{"firstName":"Konrad","lastNam e":"Kokosa"}],"lastModified":"2020-04-01T00:00:00"}]
  77. @stevejgordon www.stevejgordon.co.uk HTTP/1.1 200 OK Date: Sat, 23 May 2020

    07:20:56 GMT Content-Type: application/json; charset=utf-8 Server: Kestrel Transfer-Encoding: chunked Last-Modified: Tue, 31 Mar 2020 23:00:00 GMT [{"id":1,"title":"Pro .NET Memory Management","isbn":"978-1-4842-4026- 7","datePublished":"2018-11- 13T00:00:00","authors":[{"firstName":"Konrad","lastNam e":"Kokosa"}],"lastModified":"2020-04-01T00:00:00"}]
  78. @stevejgordon www.stevejgordon.co.uk HTTP/1.1 200 OK Date: Sat, 23 May 2020

    07:20:56 GMT Content-Type: application/json; charset=utf-8 Server: Kestrel Transfer-Encoding: chunked Last-Modified: Tue, 31 Mar 2020 23:00:00 GMT [{"id":1,"title":"Pro .NET Memory Management","isbn":"978-1-4842-4026- 7","datePublished":"2018-11- 13T00:00:00","authors":[{"firstName":"Konrad","lastNam e":"Kokosa"}],"lastModified":"2020-04-01T00:00:00"}]
  79. @stevejgordon www.stevejgordon.co.uk Summary • HTTP request received and parsed by

    Kestrel • Middleware pipeline executed • An endpoint (MVC) handles the request • Filter pipeline • Action execution • ActionResult execution
  80. THANK YOU www.stevejgordon.co.uk https://app.pluralsight.com/profile/author/steve-gordon http://bit.ly/aspnetanatomy @stevejgordon