Rescues
A rescue is an association of a special view that will only be rendered if an exception happens. The view file must be present in a rescues folder directly under your views folder.
A rescue can be associated with a controller or per action. You can also bind a rescue with an exception type. If the action throws an exception (the action cannot swallow the exception), MonoRail will match the rescue definition that is closely related to the exception type and use the specified view.
To create an association you must use the RescueAttribute. For example:
using Castle.MonoRail.Framework; [Rescue("dberror", typeof(System.Data.SqlException))] public class ProductController : Controller { [Rescue("commonerror")] public void Index() { throw new System.Data.SqlException("fake error"); } [Rescue("dumbprogrammer", typeof(DivideByZeroException))] public void List() { int val = 0; int x = 10 / val; } public void Search() { } }
The usage of the RescueAttribute in the example above defines the following rules:
- If any action throws a SqlException, the view view/rescues/dberror will be selected
- If the action Index throws any kind of exception (including SqlException), the view view/rescues/commonerror will be selected. This overrides the definition at the controller level.
- If the action List throws a DivideByZeroException, the view view/rescues/dumbprogrammer will be selected.
Whenever an exception happens, the MonoRail context (which is per request) will populate the property LastException so your view can show the exception details.
Advanced usages
Sometimes it is necessary to execute code within a rescue, a simple view is not enough (usually the case when you want to do error reporting or logging). MonoRail therefore allows you to specify a controller that is used to execute the rescue and that handles the view rendering.
A rescue controller can be specified by using the RescueAttribute
using Castle.MonoRail.Framework; [Rescue(typeof(RescueController), "RescueMethod")] public class DemoController { }
The first argument tells MonoRail what controller class to use and the second is the name of the rescue method to call. The controller must inherit from SmartDispatcherController and the method to call has to have the following signature:
public void RescueMethod(Exception exception, IController controller, IControllerContext controllerContext)
This way you can call different rescue methods for different exceptions on one controller.
Here an example of multiple rescue methods and how they can be used by your controller:
[Layout("default")] public class RescueController : SmartDispatcherController { public void GenericError(Exception exception, IController controller, IControllerContext controllerContext) { } public void NotAuthorized(Exception exception, IController controller, IControllerContext controllerContext) { } public void WcfError(Exception exception, IController controller, IControllerContext controllerContext) { } }
A controller can define different rescue methods for different types of exceptions:
public class DemoController { [Rescue(typeof(RescueController), "NotAuthorized", typeof(FaultException<OperationPermissionFault>))] [Rescue(typeof(RescueController), "Timeout", typeof(TimeoutException))] [Rescue(typeof(RescueController), "GenericError", typeof(Exception))] public void List() { } }
The rescue method signature is also defined in the IRescueController interface that can be implemented if only one rescue method needs to be present on the controller. If your rescue controller implements IRescueController you don't need to explicitly define what rescue method to use. MonoRail will default to Rescue(..)
A rescue pointing to a IRescueController would look like this:
[Rescue(typeof(RescueController))] public void List() { }