How to report error to $.ajax without throwing exc

2019-02-02 03:01发布

问题:

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.

回答1:

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.



回答2:

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


回答3:

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


回答4:

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


回答5:

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


回答6:

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


回答7:

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.