40

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.

7 Answers 7

57

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.

Sign up to request clarification or add additional context in comments.

11 Comments

I was going to accept this answer, but unfortunately it looks like MVC is rewriting this status and puts back its own message instead of the error message I want to send.
Even if you set the description in the constructor? (edited answer accordingly)
Well strange is, when I set error 500, I get Internal Server Error (Not the description I set), otherwise for 401, 400, for all requests I get 404 in the $.ajax error object.
Your answer is correct, it's bug in asp.net that it is not sending correct description, or we are missing some configuration. If microsoft will accept this as bug, I will accept your answer.
IIS does it correctly, bug was there in ASP.NET Development server.
|
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! 
    }
  }
});

1 Comment

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.
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);
            }
        }
    });
});

3 Comments

Problem comes when I do not have access to client scripts. For right now I am using same method, but it fails when I use third party client side libraries.
Do you mean the $.ajaxSetup fails to catch the exception?
No, it catches, but the error message is default set in IIS, not the one I have set.
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());

3 Comments

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.
Then define your own CustomException and catch just those via if (filterContext.Exception is MyCustomException)
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.
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;
    };

Comments

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");

1 Comment

This is the most simple way I've seen to accomplish this on the entire internet. Thank you!
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.

1 Comment

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() :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.