Reading the docs for UseExceptionHandler extension method that adds standard exception handler middleware, I see following description:
UseExceptionHandler(IApplicationBuilder)
Adds a middleware to the pipeline that will catch exceptions, log
them, and re-execute the request in an alternate pipeline. The request
will not be re-executed if the response has already started.
However I was not able docs on what this means exactly and what is an alternate pipeline
?
2
Answers
You can see what the exception handler middleware does in the source: https://github.com/dotnet/aspnetcore/blob/240377059ec25b4d9d86d4188a26722e55edc5a1/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddlewareImpl.cs#L111.
What I think they are referring to is that the middleware will reset the current HTTP context and run a new request through the middleware pipeline.
At least that is if you provide the ExceptionHandlingPath to it.
stdout
output of one command is fed as thestdin
of another, e.g.makewords sentence | lowercase | sort | unique > output.txt
ASP.NET Core’s concept of a pipeline refers to the composed sequence of middleware handlers (and other things) which you assemble during
Startup
with thoseUseSomething()
methods onWebApplicationBuilder
.Startup.Configure()
method is for: it defines the sequence of handlers that get invoked for every incoming HTTP request.Configure()
withConfigureServices()
:Configure()
is for configuring the pipeline, whileConfigureServices()
is for configuring the DI system: they’re very distinct things./api/*
as opposed to/user-visible-HTML-pages/*
Speaking generally again (so not being specific to ASP.NET Core), a common technique for composing pipelines in any modern language that supports first-class functions and/or closures is to dynamcially compose them at runtime during an initialization phase. As a concrete example, let’s look at what a statically composed (as opposed to dynamically composed) pipeline of functions would look like:
Now, to convert that
myPipeline
to use dynamic composition, those hard-coded functions need to be function-pointers, and their output still needs to be passed as input to the next function; to do this, modern libraries/frameworks will have you use a separate "builder" API/configuration-interface for composing the pipeline, so if we represented the abovemyPipeline
using something like ASP.NET Core’sConfigure()
, it would be something like this:The
builder.Use()
function would accept afunction
as a parameter and internally do something like this:Notice that the
build()
function returns a closure (thereturn function( input ) { ... }
), which captures its internalsteps
, which is the built pipeline. To "run" the pipeline you just need to invoke the returnedfunction( input ) {...}
and receive the result.With that exposition out the way…. it then follows that when ASP.NET Core uses the term "alternative pipeline" means that there exists another HTTP request+response processing pipeline which will be used to try to complete a HTTP request which failed.
This is indeed the case: when you call
.UseExceptionHandler(this IApplicationBuilder, ...)
, this is what happensUseExceptionHandler
):UseExceptionHandler
method takes your current (still-being-composed) pipeline and adds a new middleware step to it:ExceptionHandlerMiddleware
(actually, it’sExceptionHandlerMiddlewareImpl
).UseExceptionHandler
also callsapp.New()
to create a brand new pipeline from scratch, which is initially empty – then it passes the new builder for this new pipeline back to theAction<IApplicationBuilder> configure
callback so that the application-code can then add more middleware steps to it, if desired (presumably to show a user-friendly error message, etc).configure
callback returns,UseExceptionHandler
calls.Build()
on that pipeline-builder (so now it’s a baked/composed pipeline that’s ready to handle requests), and then stores that pipeline in itsExceptionHandler
field/property.ExceptionHandlerMiddlewareImpl
catches an exception, it aborts the rest of the current pipeline and instead passes the current HTTP request details to thatExceptionHandler
property – which contains an entire separate pipeline, which then (hopefully!) runs to completion to build a response for the end-user/remote-client.ExceptionHandlerMiddlewareImpl.Invoke
method, where it doesreturn this.HandleException(context, edi);
.UseExceptionHandler
means it won’t handle a second exception thrown in the secondary pipeline – for that reason, if you expect your exception-handling-pipeline to fail, you’ll want to add another call toUseExceptionHandler
inside itsconfigure
callback (though you probably shouldn’t need to do this, as exception-handling code should not raise exceptions of its own…)