[c#] Return JSON with error status code MVC

I was trying to return an error to the call to the controller as advised in This link so that client can take appropriate action. The controller is called by javascript via jquery AJAX. I am getting the Json object back only if I don't set the status to error. Here is the sample code

if (response.errors.Length > 0)
   Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(response);

I get the Json if I don't set the statuscode. If I set the status code I get the status code back but not the Json error object.

Update I want to send an Error object as JSON so that it can be handled error callback of ajax.

This question is related to c# asp.net-mvc json

The answer is


Building on the answer from Richard Garside, here's the ASP.Net Core version

public class JsonErrorResult : JsonResult
{
    private readonly HttpStatusCode _statusCode;

    public JsonErrorResult(object json) : this(json, HttpStatusCode.InternalServerError)
    {
    }

    public JsonErrorResult(object json, HttpStatusCode statusCode) : base(json)
    {
        _statusCode = statusCode;
    }

    public override void ExecuteResult(ActionContext context)
    {
        context.HttpContext.Response.StatusCode = (int)_statusCode;
        base.ExecuteResult(context);
    }

    public override Task ExecuteResultAsync(ActionContext context)
    {
        context.HttpContext.Response.StatusCode = (int)_statusCode;
        return base.ExecuteResultAsync(context);
    }
}

Then in your controller, return as follows:

// Set a json object to return. The status code defaults to 500
return new JsonErrorResult(new { message = "Sorry, an internal error occurred."});

// Or you can override the status code
return new JsonErrorResult(new { foo = "bar"}, HttpStatusCode.NotFound);

I was running Asp.Net Web Api 5.2.7 and it looks like the JsonResult class has changed to use generics and an asynchronous execute method. I ended up altering Richard Garside's solution:

public class JsonHttpStatusResult<T> : JsonResult<T>
{
    private readonly HttpStatusCode _httpStatus;

    public JsonHttpStatusResult(T content, JsonSerializerSettings serializer, Encoding encoding, ApiController controller, HttpStatusCode httpStatus) 
    : base(content, serializer, encoding, controller)
    {
        _httpStatus = httpStatus;
    }

    public override Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var returnTask = base.ExecuteAsync(cancellationToken);
        returnTask.Result.StatusCode = HttpStatusCode.BadRequest;
        return returnTask;
    }
}

Following Richard's example, you could then use this class like this:

if(thereWereErrors)
{
    var errorModel = new CustomErrorModel("There was an error");
    return new JsonHttpStatusResult<CustomErrorModel>(errorModel, new JsonSerializerSettings(), new UTF8Encoding(), this, HttpStatusCode.InternalServerError);
}

Unfortunately, you can't use an anonymous type for the content, as you need to pass a concrete type (ex: CustomErrorType) to the JsonHttpStatusResult initializer. If you want to use anonymous types, or you just want to be really slick, you can build on this solution by subclassing ApiController to add an HttpStatusCode param to the Json methods :)

public abstract class MyApiController : ApiController
{
    protected internal virtual JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus, JsonSerializerSettings serializerSettings, Encoding encoding)
    {
        return new JsonHttpStatusResult<T>(content, httpStatus, serializerSettings, encoding, this);
    }

    protected internal JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus, JsonSerializerSettings serializerSettings)
    {
        return Json(content, httpStatus, serializerSettings, new UTF8Encoding());
    }

    protected internal JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus)
    {
        return Json(content, httpStatus, new JsonSerializerSettings());
    }
}

Then you can use it with an anonymous type like this:

if(thereWereErrors)
{
    var errorModel = new { error = "There was an error" };
    return Json(errorModel, HttpStatusCode.InternalServerError);
}

Several of the responses rely on an exception being thrown and having it handled in the OnException override. In my case, I wanted to return statuses such as bad request if the user, say, had passed in a bad ID. What works for me is to use the ControllerContext:

var jsonResult = new JsonResult { JsonRequestBehavior = JsonRequestBehavior.AllowGet, Data = "whoops" };

ControllerContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;

return jsonResult;

You need to decide if you want "HTTP level error" (that what error codes are for) or "application level error" (that what your custom JSON response is for).

Most high level objects using HTTP will never look into response stream if error code set to something that is not 2xx (success range). In your case you are explicitly setting error code to failure (I think 403 or 500) and force XMLHttp object to ignore body of the response.

To fix - either handle error conditions on client side or not set error code and return JSON with error information (see Sbossb reply for details).


The neatest solution I've found is to create your own JsonResult that extends the original implementation and allows you to specify a HttpStatusCode:

public class JsonHttpStatusResult : JsonResult
{
    private readonly HttpStatusCode _httpStatus;

    public JsonHttpStatusResult(object data, HttpStatusCode httpStatus)
    {
        Data = data;
        _httpStatus = httpStatus;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        context.RequestContext.HttpContext.Response.StatusCode = (int)_httpStatus;
        base.ExecuteResult(context);
    }
}

You can then use this in your controller action like so:

if(thereWereErrors)
{
    var errorModel = new { error = "There was an error" };
    return new JsonHttpStatusResult(errorModel, HttpStatusCode.InternalServerError);
}

You have to return JSON error object yourself after setting the StatusCode, like so ...

if (BadRequest)
{
    Dictionary<string, object> error = new Dictionary<string, object>();
    error.Add("ErrorCode", -1);
    error.Add("ErrorMessage", "Something really bad happened");
    return Json(error);
}

Another way is to have a JsonErrorModel and populate it

public class JsonErrorModel
{
    public int ErrorCode { get; set;}

    public string ErrorMessage { get; set; }
}

public ActionResult SomeMethod()
{

    if (BadRequest)
    {
        var error = new JsonErrorModel
        {
            ErrorCode = -1,
            ErrorMessage = "Something really bad happened"
        };

        return Json(error);
    }

   //Return valid response
}

Take a look at the answer here as well


And if your needs aren't as complex as Sarath's you can get away with something even simpler:

[MyError]
public JsonResult Error(string objectToUpdate)
{
   throw new Exception("ERROR!");
}

public class MyErrorAttribute : FilterAttribute, IExceptionFilter
{
   public virtual void OnException(ExceptionContext filterContext)
   {
      if (filterContext == null)
      {
         throw new ArgumentNullException("filterContext");
      }
      if (filterContext.Exception != null)
      {
         filterContext.ExceptionHandled = true;
         filterContext.HttpContext.Response.Clear();
         filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
         filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
         filterContext.Result = new JsonResult() { Data = filterContext.Exception.Message };
      }
   }
}

A simple way to send a error to Json is control Http Status Code of response object and set a custom error message.

Controller

public JsonResult Create(MyObject myObject) 
{
  //AllFine
  return Json(new { IsCreated = True, Content = ViewGenerator(myObject));

  //Use input may be wrong but nothing crashed
  return Json(new { IsCreated = False, Content = ViewGenerator(myObject));  

  //Error
  Response.StatusCode = (int)HttpStatusCode.InternalServerError;
  return Json(new { IsCreated = false, ErrorMessage = 'My error message');
}

JS

$.ajax({
     type: "POST",
     dataType: "json",
     url: "MyController/Create",
     data: JSON.stringify(myObject),
     success: function (result) {
       if(result.IsCreated)
     {
    //... ALL FINE
     }
     else
     {
    //... Use input may be wrong but nothing crashed
     }
   },
    error: function (error) {
            alert("Error:" + erro.responseJSON.ErrorMessage ); //Error
        }
  });

There is a very elegant solution to this problem, just configure your site via web.config:

<system.webServer>
    <httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough"/>
</system.webServer>

Source: https://serverfault.com/questions/123729/iis-is-overriding-my-response-content-if-i-manually-set-the-response-statuscode


The thing that worked for me (and that I took from another stackoverflow response), is to set the flag:

Response.TrySkipIisCustomErrors = true;

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to asp.net-mvc

Using Lato fonts in my css (@font-face) Better solution without exluding fields from Binding Vue.js get selected option on @change You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to send json data in POST request using C# VS 2017 Metadata file '.dll could not be found The default XML namespace of the project must be the MSBuild XML namespace How to create roles in ASP.NET Core and assign them to users? The model item passed into the dictionary is of type .. but this dictionary requires a model item of type How to use npm with ASP.NET Core

Examples related to json

Use NSInteger as array index Uncaught SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) HTTP POST with Json on Body - Flutter/Dart Importing json file in TypeScript json.decoder.JSONDecodeError: Extra data: line 2 column 1 (char 190) Angular 5 Service to read local .json file How to import JSON File into a TypeScript file? Use Async/Await with Axios in React.js Uncaught SyntaxError: Unexpected token u in JSON at position 0 how to remove json object key and value.?