14

I am confused as to how to handle this situation.

Usually when an unhandled ASP.Net exception occurs, the server sends back an HTML message of some sort, either the default Asp.Net error handler or a custom error handler. In either case though, HTML is being sent back (and usually it's a good idea to make the page user friendly).

However, I am having an issue where the unhandled exceptions are occurring in Asp.net MVC controller actions that are expected to return JSON for Ajax calls. When the javascript reads the returned page (which is HTML instead of intended JSON) it crashes due to not being able to convert the response into JSON (right now I'm using ExtJS). I want Json to be returned upon an exception so that the user can be notified that an error has occurred.

The only solution I can think of is to do the following in every action that returns Json:

try { .... }
catch (Exception ex)
{
   return Json(new { success = false, msg = ex.Message });
}

I don't like that method because it requires me catch all exceptions (which is bad for obvious reasons) and it requires me to pepper every JsonResult action with that same exception handling code (which makes it hard to change later).

Is there a better method to return a more appropriate error result only on action methods that return Json, but still keep regular error pages user friendly for non-Ajax web requests?

KallDrexx
  • 26,119
  • 31
  • 137
  • 246
  • The try/catch also does not help in cases where your code "works" but the exception only happens when the framework tries to execute the ActionResult - for example if you return an object with uint64 value you get a json serialization error, even though no exception thrown in your code. Thus you need the filter or OnException handler as in accepted answer. – Etherman Sep 06 '18 at 10:11

3 Answers3

13

Here's how I solved this problem:

public class HandleJsonError : HandleErrorAttribute
{
    public override void OnException(ExceptionContext exceptionContext)
    {
        if (!exceptionContext.HttpContext.Request.IsAjaxRequest() || exceptionContext.Exception == null) return;

        exceptionContext.HttpContext.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
        exceptionContext.Result = new JsonResult
                                   {
                                       Data = new
                                                  {
                                                      exceptionContext.Exception.Message,
                                                      exceptionContext.Exception.StackTrace
                                                  }
                                   };

        exceptionContext.ExceptionHandled = true;
    }
}
adamjford
  • 7,241
  • 5
  • 26
  • 40
  • 6
    You might want to inherit from HandleErrorAttribute instead and override OnException. That way, your filter will only fire when there is an exception, rather than for every request. – Paul Hiles Jan 19 '11 at 15:45
  • Action filters seem to be the way to go. Marking this as the answer due to code! – KallDrexx Jan 19 '11 at 15:59
  • @The Flower Guy: Oh, huh, that sounds right. I'll give a shot when I get a chance. Thanks! – adamjford Jan 19 '11 at 16:26
  • @KallDrexx This is only half the solution, isn't it? What are you doing on the client side? – bzlm Feb 02 '11 at 18:21
  • 1
    Client side is already set up to determine if an 500 error occurred and parse any json it receives. The problem was that before this solution, the server would only return HTML error responses and I needed a JSON error response. – KallDrexx Feb 02 '11 at 18:37
  • for anyone wondering what `ErrorSignal` is it's from the Elmah error handling framework. See http://stackoverflow.com/questions/7441062 – Simon_Weaver Jun 26 '13 at 23:05
  • Oh, good call! No idea why I didn't clarify that or take that line out when I made this answer. I'll update it. – adamjford Jun 26 '13 at 23:07
  • 1
    @adamjford fortunately I'm already using Elmah so I knew what it was. I'm glad you did add this because it's very important to log the error manually because if you're setting ExceptionHandling=true that prevents Elmah from kicking in. And ExceptionHandling=true seems necessary or else you get the default HTML 500 response – Simon_Weaver Jun 26 '13 at 23:58
4

You could override OnException in the controller and put in some logic to return a JSON error response which you would need to handle in your javascript calling code:

protected override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = Json("...");
        }
        else
        {
            base.OnException(filterContext);
        }
    }

If you want to do it 'properly', you can write an actionfilter with pretty much the same code and apply it to all controllers. If you are using MVC3, you can make it a global filter so it will apply to all controllers.

Paul Hiles
  • 8,862
  • 6
  • 45
  • 74
1

You can use a custom ActionFilter and then inside of that if it was a javascript request you can then format the response as a JSON object instead of an HTML page. HandleError would be a good place to look at extending your base class from.

An alternative option if you didn't want to do an actio filter would be to override OnException in your controller and set exceptionContext.Result to a new JsonResult that indicates failure.

Aaron Weiker
  • 2,433
  • 2
  • 21
  • 22