Search code examples
c#asp.net-mvcchild-actions

Is there a good way to handle exceptions in MVC ChildActions


I seem to be doing a lot of Exception swallowing with Child Actions.

    [ChildActionOnly]
    [OutputCache(Duration = 1200, VaryByParam = "key;param")]
    public ActionResult ChildPart(int key, string param)
    {
        try
        {
            var model = DoRiskyExceptionProneThing(key, param)
            return View("_ChildPart", model);
        }
        catch (Exception ex)
        {
            // Log to elmah using a helper method
            ErrorLog.LogError(ex, "Child Action Error ");

            // return a pretty bit of HTML to avoid a whitescreen of death on the client
            return View("_ChildActionOnlyError");
        }
    }

I feel like I'm cutting and pasting heaps of code, and with each cut an paste we all know a kitten is being drowned in angels tears.

Is there a better way to manage exceptions in child actions that would allow the rest of the screen to render appropriately?


Solution

  • You could create a CustomHandleError attribute based on Mvc's HandleError attribute, override the OnException method, do your logging and possibly return a custom view.

    public override void OnException(ExceptionContext filterContext)
    {
        // Log to elmah using a helper method
        ErrorLog.LogError(filterContext.Exception, "Oh no!");
    
        var controllerName = (string)filterContext.RouteData.Values["controller"];
        var actionName = (string)filterContext.RouteData.Values["action"];
    
        if (!filterContext.HttpContext.IsCustomErrorEnabled)
        {
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();
            filterContext.HttpContext.Response.StatusCode = 500;
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
    
            var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
            filterContext.Result = new ViewResult
            {
                ViewName = "_ChildActionOnlyError",
                MasterName = Master,
                ViewData = new ViewDataDictionary(model),
                TempData = filterContext.Controller.TempData
            };
            return;
        }
    }
    

    Then decorate any controllers and/or actions that you want to enable with this logic like so:

    [ChildActionOnly]
    [OutputCache(Duration = 1200, VaryByParam = "key;param")]
    [CustomHandleError]
    public ActionResult ChildPart(int key, string param)
    {
        var model = DoRiskyExceptionProneThing(key, param)
        return View("_ChildPart", model);
    }