{"id":84829,"date":"2019-07-29T13:59:36","date_gmt":"2019-07-29T13:59:36","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=84829"},"modified":"2022-04-24T20:54:57","modified_gmt":"2022-04-24T20:54:57","slug":"asp-net-core-3-0-exception-handling","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/asp-net-core-3-0-exception-handling\/","title":{"rendered":"ASP.NET Core 3.0 Exception Handling"},"content":{"rendered":"<p>An aspect of the ASP.NET Core platform that I have loved since the first version is that the team attempted to engineer some of the common best practices of previous ASP.NET development right in the framework. This is particularly evident for exception handling.<\/p>\n<p>When it comes to dealing with the things that could go wrong in your application, the big issue is not using <code>try\/catch<\/code> blocks wherever it makes sense, but making sure you can effectively trap whatever exception that could be thrown but may go unnoticed and unhandled by the application. Since the early days of programming (remember Visual Basic\u2019s <code>OnError<\/code>?), it\u2019s always been a good practice to define a sort of a safety net around the application to intercept whatever exception bubbles up outside the control of the calling context.<\/p>\n<p>In this article, I\u2019ll explain the facts of exception handling as you would set it up in ASP.NET Core 3.0.<\/p>\n<h2>The ASP.NET Safety Net<\/h2>\n<p>Whether Web Forms or MVC, classic ASP.NET provides a global error handler function in the folds of the <em>global.asax<\/em> file\u2014the <code>Application_Error<\/code> method. Any code you use to fill the method is invoked just before the system displays the nefarious yellow page of death or lets the web server deal with an error HTTP status code. In other words, the code you put in the body of the <code>Application_Error<\/code> method represents your last chance to fix things before the end.<\/p>\n<p>What kind of code should you have, however, in a global error handler?<\/p>\n<p>Intelligently, classic ASP.NET let us define error paths in the <em>web.config<\/em> file and uses those paths to re-route the user to a different page. The path you specify is ultimately a URL and, as such, can point to a static or even a dynamic endpoint. A static endpoint (e.g., an HTML page) can\u2019t do much for the end-user but profoundly apologise for the (largely unknown) error. A dynamic endpoint (an ASPX file or a controller route) can, in theory, tailor an up-close-and-personal message but only if it can gain access to the specific situation that generated the error.<\/p>\n<p>In years of ASP.NET programming, we have first learned how to retrieve the last occurred exception and redirect back to the most appropriate error page. This ultimately resulted in a flexible error router dispatching to a myriad of error pages. The sore point of global exception handling, in fact, is that you discover the error in one request but need to place another request to the system to have the error message rendered in a user-friendly and nice-looking error page. The <code>Server.TransferRequest<\/code> method was introduced for that purpose. The method works like a canonical redirect except that it takes place internally and no interaction ever takes place with the browser. In addition to that, in ASP.NET MVC you can even find a way to place an analogous internal URL request that the action invoker stack would process as if it were originated by an external request. As a result, you get an exception, and then some code of yours runs with full access to the details of the exception and the power to display some user interface at leisure.<\/p>\n<p>In the end, building a safety net around the application was a problem solved in ASP.NET and ASP.NET MVC. In ASP.NET Core, the way you address it is different and passes through a new type of middleware.<\/p>\n<h2>The Exception Handling Middleware<\/h2>\n<p>In ASP.NET Core 3.0, you install the exception handling middleware and let it capture any unhandled exceptions and redirect to the most appropriate endpoint. As mentioned, the redirect isn\u2019t physical\u2014no HTTP 302 request is ever sent\u2014but logical, and the browser is never involved with this.<\/p>\n<p>There are two types of exception handling middleware: one is targeted to developers and is ideal for development scenarios, and one is suitable for staging and production environments and, as such, targeted to end-users.<\/p>\n<p>In a development scenario, you might want to have the original error message displayed, and you even want the stack trace available and a snapshot of the HTTP context with details of the query string, session state, cookies and the like. This is just the kind of thing labelled as the \u201cyellow page of death\u201d in older versions of ASP.NET.<\/p>\n<p>To have that, you append the following piece of middleware to the ASP.NET Core runtime pipeline. In <strong>startup.cs<\/strong>, you add the following:<\/p>\n<pre class=\"lang:none theme:none\">app.UseDeveloperExceptionPage();<\/pre>\n<p>As you can see, the middleware is parameter-less and doesn\u2019t allow routing to a custom page or view. The middleware, instead, prepares a system page on the fly that contains the details of the last intercepted exception and a snapshot of system status at the time of the exception. It\u2019s a terminating middleware in the sense that it stops the request and represents the new \u201cwhite page of death\u201d\u2014yes, they changed the background colour!<\/p>\n<p>Hand in hand with this middleware, you can also use the <code>UseExceptionHandler<\/code> middleware that, instead, is ideal for production-ready exception handling.<\/p>\n<h2>The <em>UseExceptionHandler<\/em> Middleware<\/h2>\n<p>You add the <code>UseExceptionHandler <\/code>middleware to the pipeline using the <code>Configure<\/code> method of the startup class. Here\u2019s an example:<\/p>\n<pre class=\"lang:c# theme:vs2012\">public class Startup\r\n{\r\n    public void Configure(IApplicationBuilder app)\r\n    {\r\n        app.UseExceptionHandler(\"\/system\/error\");\r\n        ...\r\n    }\r\n}<\/pre>\n<p>The <code>UseExceptionHandler<\/code> extension method takes a URL and logically redirects the flow of the application to it. More in detail, the middleware places a new request for the specified URL right into the ASP.NET pipeline.<\/p>\n<p>As the two middleware blocks address distinct scenarios\u2014one for development and one for production\u2014you might want to programmatically switch between middleware at runtime. You just use the services of the hosting environment API.<\/p>\n<pre class=\"lang:none theme:none\">public void Configure(\r\n      IApplicationBuilder app, IHostingEnvironment env)\r\n{\r\n    if (env.IsDevelopment())\r\n    {\r\n        app.UseDeveloperExceptionPage();\r\n    } \r\n    else \r\n    {\r\n        app.UseExceptionHandler(\"~\/system\/error\");\r\n    }\r\n    ...\r\n}<\/pre>\n<p>The methods of the <code>IHostingEnvironment<\/code> interface let you detect the current environment and intelligently decide which exception middleware to turn on. Based on the listing above, the <code>Error<\/code> method on the <code>System<\/code> controller is invoked to deal with any unhandled exceptions.<\/p>\n<p>The next point to address is, what should you do when an exception is thrown and travels unhandled up to the safety net?<\/p>\n<h2>Handling the Exception<\/h2>\n<p>Looking at the code snippet above, the <code>Error<\/code> method is invoked without explicit parameters. In the past, the <code>GetLastError<\/code> method on the <code>Server<\/code> intrinsic object helped to retrieve the last exception occurred. No such object exists anymore in ASP.NET Core, but the new <code>Features<\/code> property on the HTTP context object allows to retrieve runtime information such as the last exception occurred and related information. Here\u2019s the code you need to have in the global error handler.<\/p>\n<pre class=\"lang:none theme:none \">public IActionResult Error()\r\n{\r\n    \/\/ Retrieve error information in case of internal errors\r\n    var error = HttpContext\r\n              .Features\r\n              .Get&lt;IExceptionHandlerFeature&gt;();\r\n    if (error == null)\r\n         return View(model);\r\n\r\n    \/\/ Use the information about the exception \r\n    var exception = error.Error;\r\n    ...\r\n}<\/pre>\n<p>You query the HTTP context of the current request for the latest exception and receive an object that implements the <code>IExceptionHandlerFeature<\/code> interface. The object exposes a single property named <code>Error<\/code> which points you right to the last tracked exception.<\/p>\n<p>The interesting part is that once you hold in your hands a valid exception object, then you have all the available information about what could have gone wrong and can arrange the most appropriate response\u2014whether some plain text or a full-blown, nice-looking error page. You can also decide to swallow most of the details contained in the exception object and route to a few of the predefined (and more generic) error message. In other words, you\u2019re now in total control of what you display to your users.<\/p>\n<p>You are so much in control that you could even throw an exception any time that a business operation goes wrong or can\u2019t be performed for whatever reason. Instead of returning error codes all the way up, from the inner layers to the presentation, you throw an exception, and you\u2019re done. To gain even more control, you can, for example, build your own hierarchy of exception objects, specific to the application. (I\u2019ll return on this in a moment.)<\/p>\n<h2>Referencing the Request<\/h2>\n<p>The <code>IExceptionHandlerFeature<\/code> interface only returns information about the exception that was thrown at some point but nothing about the URL in the execution of which the exception threw. To get that, you need to request the <code>IExceptionHandlerPathFeature<\/code> interface to the HTTP context object, as shown below.<\/p>\n<pre class=\"lang:none theme:none\">public IActionResult Error()\r\n{\r\n    \/\/ Retrieve error information in case of internal errors\r\n    var path = HttpContext\r\n              .Features\r\n              .Get&lt;IExceptionHandlerPathFeature&gt;();\r\n    if (path == null)\r\n         return View(model);\r\n\r\n    \/\/ Use the information about the exception \r\n    var exception = path.Error;\r\n    var pathString = path.Path;\r\n    ...\r\n}<\/pre>\n<p>Note that the <code>IExceptionHandlerPathFeature<\/code> interface inherits from the aforementioned <code>IExceptionHandlerFeature<\/code> meaning that with a single request you get both the exception and path information.<\/p>\n<h2>What About 404 Errors?<\/h2>\n<p>The safety net captures any exceptions whether they originate in the processing of the request or the preparation of it, such as during model binding or in case of a missing route. With the approach shown earlier, however, you can\u2019t get to know about the specific HTTP status code returned by the pipeline without an exception being thrown. The most notable example is a 404, but also any request that returns an error HTTP code such as 500.<\/p>\n<p>To be sure your code can handle any HTTP status code of interest, you should add another piece of middleware to the pipeline.<\/p>\n<pre class=\"lang:none theme:none\">app.UseStatusCodePages();<\/pre>\n<p>The status code middleware is another terminating middleware that directly returns some output to the end-user. In particular, it writes a plain text string to the output stream with the error code and the basic description. To handle the HTTP status code programmatically, you need another piece of middleware on top of the exception handler.<\/p>\n<pre class=\"lang:none theme:none\">App.UseStatusCodePagesWithReExecute(\"\/system\/error\/{0}\");<\/pre>\n<p>The parameter {0} will be set with the HTTP status code if any. The re-execute in the name of the method just denotes that the request that generated a response with an error status code will be logically redirected (re-execute means no 302 to the browser) to the specified URL. Here\u2019s how to rewrite the method to display the above error page to the user.<\/p>\n<pre class=\"lang:none theme:none\">public IActionResult Error(\r\n     [Bind(Prefix = \"id\")] int statusCode = 0)\r\n{\r\n    \/\/ Switch to the appropriate page\r\n    switch(statusCode)\r\n    {\r\n        case 404:\r\n           return Redirect(...);\r\n        ...\r\n    }\r\n\r\n\r\n    \/\/ Retrieve error information in case of internal errors\r\n    var path = HttpContext\r\n              .Features\r\n              .Get&lt;IExceptionHandlerPathFeature&gt;();\r\n    if (path == null)\r\n         return View(model);\r\n\r\n    \/\/ Use the information about the exception \r\n    var exception = path.Error;\r\n    var pathString = path.Path;\r\n    ...\r\n}<\/pre>\n<p>The HTTP status code is received through the model binding layer, and, if it matches one of those, the method can handle it and select the appropriate action. Note that the way in which the status code parameter is bound to the method is up to you. The approach shown above of using an <code>id<\/code> parameter is only an example.<\/p>\n<h2>Going One Step Further<\/h2>\n<p>How would you arrange the error page? Aside from some standard graphical template, what would you put in it? For sure, you want to render some message, whether the native message of the caught exception or some other more generic message. Anything else you would do?<\/p>\n<p>Personally, I tend to have two levels of messages in any exception and a list of recovery links. The first level message is the native message of the exception (or any other programmatically selected message related to the error occurred). The second level message is any other descriptive message you might optionally want to display.<\/p>\n<p>A recovery link, instead, is an application URL (or even an external URL) that you add to the error page to let users resume with the application from one or more safe places. Typically, one recovery link can be the home page. Recovery links should be bound to each exception type so that you specify them by the time you throw.<\/p>\n<p>The idea is to set a base exception type on a per-application basis. For example, you can have a <code>MyAppException<\/code> base class as below:<\/p>\n<pre class=\"lang:none theme:none\">public class MyAppException : Exception\r\n{\r\n   public MyAppException()\r\n   {\r\n       RecoveryLinks = new List&lt;RecoveryLink&gt;();\r\n   }\r\n   public MyAppException(string message) : base(message)\r\n   {\r\n       RecoveryLinks = new List&lt;RecoveryLink&gt;();\r\n   }\r\n   public MyAppException(Exception exception) : \r\n                      this(exception.Message)\r\n   {\r\n   }\r\n   public List&lt;RecoveryLink&gt; RecoveryLinks { get; }\r\n   public MyAppException AddRecoveryLink(\r\n           string text, string url)\r\n   {\r\n        RecoveryLinks.Add(new RecoveryLink(text, url));\r\n        return this;\r\n   }\r\n   public MyAppException AddRecoveryLink(RecoveryLink link)\r\n   {\r\n        RecoveryLinks.Add(link);\r\n        return this;\r\n   }\r\n}<\/pre>\n<p>The <code>RecoveryLink<\/code> class is a custom class with a couple of properties\u2014text and URL. Any other application-specific exception will inherit from <code>MyAppException<\/code> and will have available handy methods to deal with recovery links.<\/p>\n<pre class=\"lang:none theme:none\">public IActionResult Throw()\r\n{\r\n    throw new MyAppException(\"A severe error occurred\")\r\n        .AddRecoveryLink(\"Google\", \"http:\/\/www.google.com\");\r\n}<\/pre>\n<p>In the exception handler, you just try to cast the exception object retrieved to <code>MyAppException<\/code>. If successful, you can then access the recovery links and use them in the user interface.<\/p>\n<pre class=\"lang:none theme:none\">&lt;ul&gt;\r\n@foreach(var link in Model.Error.RecoveryLinks)\r\n{\r\n    &lt;li&gt;&lt;a href=\"@link.Url\"&gt;@link.Text&lt;\/a&gt;&lt;\/li&gt;\r\n}\r\n&lt;\/ul&gt;<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"915\" height=\"598\" class=\"wp-image-84830\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-18.jpeg\" \/><\/p>\n<p><em>FIGURE 1-A sample error page using exception handlers and recovery links.<\/em><\/p>\n<h2>Summary<\/h2>\n<p>When it occurs, an exception breaks the normal flow of execution of a program and causes a re-routing. If you foresee that something could go wrong at some specific point, then the best option is probably using a try\/catch block to wrap the risky code. However, sometimes, exceptions occur inadvertently and having a safety net around the application is quite useful. In this article, I\u2019ve reviewed the ASP.NET Core 3.0 equipment for global error handling and suggested a couple of improvements for smoothing exception handling in real-world applications.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dealing with errors is one of the essential elements of programming applications. The way to do so has changed over the years and versions of ASP.NET. In this article, Dino Esposito discusses how handle exceptions with ASP.NET Core 3.0.&hellip;<\/p>\n","protected":false},"author":221911,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[],"coauthors":[6780],"class_list":["post-84829","post","type-post","status-publish","format-standard","hentry","category-dotnet-development"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/84829","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221911"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=84829"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/84829\/revisions"}],"predecessor-version":[{"id":84832,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/84829\/revisions\/84832"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=84829"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=84829"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=84829"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=84829"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}