Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return JSON from a HandleError filter?

Tags:

asp.net-mvc

aspnet mvc has the HandleError filter that will return a view if an error occurs, but if an error occurs when calling a JsonResult Action how can I return a JSON object that represents an error?

I don't want to wrap the code in each action method that returns a JsonResult in a try/catch to accomplish it, I'd rather do it by adding a 'HandleJsonError' attribute or using the existing HandleError attribute to the required action methods.

like image 309
Luke Smith Avatar asked Sep 21 '08 00:09

Luke Smith


3 Answers

In short, the way to go can be to extend the HandleErrorAttribute, like this:

public class OncHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        // Elmah-Log only handled exceptions
        if (context.ExceptionHandled)
            ErrorSignal.FromCurrentContext().Raise(context.Exception);

        if (context.HttpContext.Request.IsAjaxRequest())
        {
            // if request was an Ajax request, respond with json with Error field
            var jsonResult = new ErrorController { ControllerContext = context }.GetJsonError(context.Exception);
            jsonResult.ExecuteResult(context);
            context.ExceptionHandled = true;
        }
        else
        {
            // if not an ajax request, continue with logic implemented by MVC -> html error page
            base.OnException(context);
        }
    }
}

Remove Elmah logging code line if you don't need it. I use one of my controllers to return a json based on an error and context. Here is the sample:

    public class ErrorController : Controller
{
    public ActionResult GetJsonError(Exception ex)
    {
        var ticketId = Guid.NewGuid(); // Lets issue a ticket to show the user and have in the log

        Request.ServerVariables["TTicketID"] = ticketId.ToString(); // Elmah will show this in a nice table

        ErrorSignal.FromCurrentContext().Raise(ex); //ELMAH Signaling

        ex.Data.Add("TTicketID", ticketId.ToString()); // Trying to see where this one gets in Elmah

        return Json(new { Error = String.Format("Support ticket: {0}\r\n Error: {1}", ticketId, ex.ToString()) }, JsonRequestBehavior.AllowGet);
    }

I add some ticket info above, you can ignore this. Due to the way the filter is implemented (extends the default HandleErrorAttributes) we can remove then HandleErrorAttribute from the global filters:

    public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new GlobalAuthorise());
        filters.Add(new OncHandleErrorAttribute());
        //filters.Add(new HandleErrorAttribute());
    }

This is basically it. You can read my blog entry for more detailed info, but for the idea, above should suffice.

like image 83
Stanislav Dvoychenko Avatar answered Nov 11 '22 20:11

Stanislav Dvoychenko


Take a look at the MVC implementation of HandleErrorAttribute. It returns a ViewResult. You could write your own version (HandleJsonErrorAttribute) that returns a JsonResult.

like image 20
Haacked Avatar answered Nov 11 '22 20:11

Haacked


Maybe you could create your own Attribute and have a constructor value that takes an enum value of View or Json. Below is what Im using for a custom Authorization Attribute to demonstrate what I mean. This way when authentication fails on a json request it responds with a json error and the same with if it returns a View.

   public enum ActionResultTypes
   {
       View,
       Json
   }

    public sealed class AuthorizationRequiredAttribute : ActionFilterAttribute, IAuthorizationFilter
    {
        public ActionResultTypes ActionResultType { get; set; }

        public AuthorizationRequiredAttribute(ActionResultTypes actionResultType)
        {
            this.ActionResultType = ActionResultType;
        }
    }

    //And used like
    [AuthorizationRequired(ActionResultTypes.View)]
    public ActionResult About()
    {
    }
like image 1
Vyrotek Avatar answered Nov 11 '22 20:11

Vyrotek