Sometimes we implement page controllers with custom logic that may throw various exceptions. It could be pages that expect certain parameters that are not met at runtime, or an order flow page that is requested with an empty shopping cart or a cart in a state not valid for that specific page.
How can the exceptions be generically handled so they show a friendly error message to visitors, and a detailed stack trace to certain user groups?
The key thing here is that the controller or a service method is throwing an exception that can be handled by an Exception Filter. ASP.Net MVC already gives us a HandleErrorAttribute, which I have chosen to override and extend in this post.
This exception filter should render a friendly error message (a CMS text) in place of the requested page. If the request came as an AJAX request, then it should return a JSON object with the same error message, for a frontend application to show. In this way a group of pre-approved users would be able to look at stack traces in production environments, while others would not.
The implemented rules are that a friendly error message should be returned, unless:
- The exception was already handled or came from a child action method.
- The website runs in debug mode.
- Custom errors are enabled at IIS level.
- The exception would not translate to HTTP status code 500.
- The exception is a sub-class of the type specified in the property ExceptionType (defaults to Exception).
- The current user principal is allowed to see detailed error messages. This is configured in EPiServer, on the Permission to Functions configuration page.
This exception filter attribute can be added to a base controller class or to specific controller classes, and whenever an exception is thrown it will be passed to this exception filter.
A sweet thing about this generic implementation is that you could actually add multiple instances of this attribute to a controller class, with different types in the ExceptionType property. This way you could show different messages to different error causes. It could be one message if backend systems times out, another one if some parameters were invalid for a certain operation, etc.
My solution is implemented on top of EPiServer's Alloy Tech starter website. I chose to add a translation text to the resource XML file. But the error messages might as well be added to a settings page, as start page, an order flow container page etc. If the error message properties got extracted to an interface, it could even be injected into the attribute. However, you would have to limit yourself to use a pre-selected content instance and use strings to pass the name of the error message property.
Also note that if the request is an AJAX request the exception filter will not return a complete HTML document, but only a JSON result with a simple error message string which can be shown by a frontend application. If the user is logged into EPiServer and have got permissions to see detailed error messages, the JSON result will also contain data about the handled exception.
Source code for the Exception Filter attribute:
An example of applying the attribute on a base controller: