39

I have a controller, and a method as defined...

[HttpPost]
public ActionResult UpdateUser(UserInformation model){

   // Instead of throwing exception
   throw new InvalidOperationException("Something went wrong");


   // I need something like 
   return ExecutionError("Error Message");

   // which should be received as an error to my 
   // $.ajax at client side...

}

Problems with Exceptions

  1. We have to log exceptions in case of device or network errors like SQL Connectivity errors.
  2. These messages are like validation messages for users, we dont want to log.
  3. Throwing exceptions also floods Event Viewer.

I need some easy way to report some custom http status to my $.ajax call so that it should result an error at client side, but I do not want to throw an error.

UPDATE

I cannot change client script because it becomes inconsistent with other data source.

So far, HttpStatusCodeResult should work but it's IIS that is causing the problem here. No matter what error message I set, tried all answers, still I receive default message only.

Akash Kava
  • 39,066
  • 20
  • 121
  • 167

7 Answers7

55

This is where HTTP status codes come into play. With Ajax you will be able to handle them accordingly.

[HttpPost]
public ActionResult UpdateUser(UserInformation model){
    if (!UserIsAuthorized())
        return new HttpStatusCodeResult(401, "Custom Error Message 1"); // Unauthorized
    if (!model.IsValid)
        return new HttpStatusCodeResult(400, "Custom Error Message 2"); // Bad Request
    // etc.
}

Here's a list of the defined status codes.

Dennis Traub
  • 50,557
  • 7
  • 93
  • 108
9

Description

What about returning an object back to your page and analyse that in your ajax callback.

Sample

[HttpPost]
public ActionResult UpdateUser(UserInformation model)
{
    if (SomethingWentWrong)
        return this.Json(new { success = false, message = "Uuups, something went wrong!" });

    return this.Json(new { success=true, message=string.Empty});
}

jQuery

$.ajax({
  url: "...",
  success: function(data){
    if (!data.success) 
    {
       // do something to show the user something went wrong using data.message
    } else {
       // YES! 
    }
  }
});
Community
  • 1
  • 1
dknaack
  • 60,192
  • 27
  • 155
  • 202
  • I was using exactly like this, but it makes me change existing scripts and also force other developers to write more, instead I only want to do anything on server side. – Akash Kava Jan 02 '12 at 15:13
  • 1
    Simple one. Thanks for sharing dknaack – sivaL Nov 21 '19 at 22:36
6

You can create a helper method in a base controller that will return an server error but with your custom status code. Example:

public abstract class MyBaseController : Controller
{
    public EmptyResult ExecutionError(string message)
    {
        Response.StatusCode = 550;
        Response.Write(message);
        return new EmptyResult();
    }
}

You will call this method in your actions when needed. In your example:

[HttpPost]
public ActionResult UpdateUser(UserInformation model){

   // Instead of throwing exception
   // throw new InvalidOperationException("Something went wrong");


   // The thing you need is 
   return ExecutionError("Error Message");

   // which should be received as an error to my 
   // $.ajax at client side...

}

The errors (including the custom code '550') can be handled globally on client side like this:

$(document).ready(function () {
    $.ajaxSetup({
        error: function (x, e) {
            if (x.status == 0) {
                alert('You are offline!!\n Please Check Your Network.');
            } else if (x.status == 404) {
                alert('Requested URL not found.');
/*------>*/ } else if (x.status == 550) { // <----- THIS IS MY CUSTOM ERROR CODE
                alert(x.responseText);
            } else if (x.status == 500) {
                alert('Internel Server Error.');
            } else if (e == 'parsererror') {
                alert('Error.\nParsing JSON Request failed.');
            } else if (e == 'timeout') {
                alert('Request Time out.');
            } else {
                alert('Unknow Error.\n' + x.responseText);
            }
        }
    });
});
shizik
  • 910
  • 6
  • 16
2

This is a class I wrote the sends exceptions back to ajax requests as JSON

public class FormatExceptionAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext filterContext)
        {
            if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.Result = new JsonResult()
                                        {
                                            ContentType = "application/json",
                                            Data = new
                                                    {
                                                        name = filterContext.Exception.GetType().Name,
                                                        message = filterContext.Exception.Message,
                                                        callstack = filterContext.Exception.StackTrace
                                                    },
                                            JsonRequestBehavior = JsonRequestBehavior.AllowGet
                                        };

                filterContext.ExceptionHandled = true;
                filterContext.HttpContext.Response.StatusCode = 500;
                filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; 
            }
            else
            {
                base.OnException(filterContext);
            }
        }
    }

It gets registered with MVC in your application's Global.asax.cs file like so:

GlobalFilters.Filters.Add(new FormatExceptionAttribute());
Jason
  • 15,915
  • 3
  • 48
  • 72
  • I like the filter, but this way it will eat up all exceptions, I do not want to throw and catch, instead I want generic execution logic where if error is only set by me, I want to report it. – Akash Kava Jan 02 '12 at 15:11
  • 2
    Then define your own CustomException and catch just those via if (filterContext.Exception is MyCustomException) – BigMike Jan 02 '12 at 15:15
  • Well I still do not prefer to use exception as part of logic, I have server handling 100s of request per second, throwing exceptions slows down execution instead of properly finishing call. And it is bad for reusablity as in future some other third party filter or anyone trying to catch exception can break my logic. – Akash Kava Jan 03 '12 at 07:45
1

Best way I have found is as follows:

// Return error status and custom message as 'errorThrown' parameter of ajax request
return new HttpStatusCodeResult(400, "Ajax error test");
GP24
  • 867
  • 2
  • 13
  • 28
1

Based on what BigMike posted, this was what I did in my NON MVC/WEBAPI webproject.

Response.ContentType = "application/json";
Response.StatusCode = 400;
Response.Write (ex.Message);

For what it is worth (and thanks BigMike!) It worked perfectly.

roblem
  • 527
  • 5
  • 8
  • Ah, this did the trick for me! Thanks. I just made a ControllerExtension which returns a new ContentResult. But inside I applied the code above. I just need to pass two parameters: statuscode and message. Now I'm able to catch this easily inside $.ajax().error() :) – Rob van Meeuwen Jan 31 '17 at 09:32
1

I use this Particular class for Ajax errors

public class HttpStatusCodeResultWithJson : JsonResult
{
    private int _statusCode;
    private string _description;
    public HttpStatusCodeResultWithJson(int statusCode, string description = null)
    {
        _statusCode = statusCode;
        _description = description;
    }
    public override void ExecuteResult(ControllerContext context)
    {
        var httpContext = context.HttpContext;
        var response = httpContext.Response;
        response.StatusCode = _statusCode;
        response.StatusDescription = _description;
        base.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
        base.ExecuteResult(context);
    }
}

Status code is a custom HTTP Status Code and in a global Ajax Error Function I test it like:

MyNsp.ErrorAjax = function (xhr, st, str) {
        if (xhr.status == '418') {
            MyNsp.UI.Message("Warning: session expired!");
            return;
        }
        if (xhr.status == "419") {
            MyNsp.UI.Message("Security Token Missing");
            return;
        }
        var msg = 'Error: ' + (str ? str : xhr.statusText);
        MyNsp.UI.Message('Error. - status:' + st + "(" + msg + ")");
        return;
    };
BigMike
  • 6,683
  • 1
  • 23
  • 24