Pro Yearly is on sale from $80 to $50! »

Anatomy of ASP.NET Core Requests (60m)

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

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 (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 (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 (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 (blank line)
  9. KESTREL

  10. 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
  11. 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
  12. 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
  13. @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(); }); }
  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 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
  17. @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
  18. @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
  19. @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); }); ... }
  20. @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); }); ... }
  21. @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); }); ... }
  22. @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); }); ... }
  23. @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); } }
  24. @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); } }
  25. @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); } }
  26. @stevejgordon www.stevejgordon.co.uk public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseMiddleware<MetricMiddleware>();

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

    UseMetrics(this IApplicationBuilder builder) { return builder.UseMiddleware<MetricMiddleware>(); } }
  28. @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(); ... }
  29. ENDPOINT ROUTING

  30. @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(); }); }
  31. @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(); }); }
  32. @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(); }); } Routing Zone
  33. @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(); }); } Endpoint Aware
  34. DEMO

  35. ASP.NET CORE MVC

  36. @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()); } ... }
  37. @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()); } ... }
  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 Controller Initialisation Action Execution View Rendering Result Execution

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

    e s p o n s e Middleware Forbidden void OnAuthorization(AuthorizationFilterContext ctx)
  43. 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);
  44. DEMO

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

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

    { app.UseResponseCompression(); } }
  47. @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 }
  48. @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 }
  49. Resource Filters Middleware Filters Controller Created Authorization Filters R e

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

  51. 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
  52. MODEL BINDERS

  53. @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
  54. @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
  55. @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
  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 [HttpPost] public IActionResult CreateBook(BookInputModel book) { // Save

    the Book... return Ok(); } public class BookInputModel { public int Id { get; set; } public string Title { get; set; } public string ISBN { get; set; } }
  59. INPUT FORMATTERS

  60. DEMO

  61. 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);
  62. 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);
  63. DEMO

  64. 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);
  65. DEMO

  66. 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);
  67. 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
  68. 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 void OnResultExecuted(ResultExecutedContext ctx);
  69. DEMO

  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. @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
  72. 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
  73. @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"}]
  74. @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"}]
  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 Summary • HTTP request received and parsed by

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