Подтвердить что ты не робот

Возврат JSON с кодом состояния ошибки MVC

Я пытался вернуть ошибку на вызов контроллера, как указано в Эта ссылка, чтобы клиент мог предпринять соответствующие действия. Контроллер вызывается javascript через jquery AJAX. Я возвращаю объект Json только в том случае, если я не установил статус ошибки. Вот пример кода

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

Я получаю Json, если я не устанавливаю код состояния. Если я установил код состояния, я получу код состояния, но не объект ошибки Json.

Обновление Я хочу отправить объект Error как JSON, чтобы обработать обратный вызов ajax.

4b9b3361

Ответ 1

Я нашел решение здесь

Мне пришлось создать фильтр действий, чтобы переопределить поведение MVC по умолчанию

Вот мой класс исключения

class ValidationException : ApplicationException
{
    public JsonResult exceptionDetails;
    public ValidationException(JsonResult exceptionDetails)
    {
        this.exceptionDetails = exceptionDetails;
    }
    public ValidationException(string message) : base(message) { }
    public ValidationException(string message, Exception inner) : base(message, inner) { }
    protected ValidationException(
    System.Runtime.Serialization.SerializationInfo info,
    System.Runtime.Serialization.StreamingContext context)
        : base(info, context) { }
}

Обратите внимание, что у меня есть конструктор, который инициализирует мой JSON. Вот фильтр действий

public class HandleUIExceptionAttribute : 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 = ((ValidationException)filterContext.Exception).myJsonError;
        }
    }

Теперь, когда у меня есть фильтр действий, я украшу свой контроллер атрибутом фильтра

[HandleUIException]
public JsonResult UpdateName(string objectToUpdate)
{
   var response = myClient.ValidateObject(objectToUpdate);
   if (response.errors.Length > 0)
     throw new ValidationException(Json(response));
}

При возникновении ошибки активируется фильтр действий, который реализует IExceptionFilter, и я возвращаю Json на клиенте при обратном вызове ошибки.

Ответ 2

Самое лучшее решение, которое я нашел, - создать свой собственный JsonResult, который расширяет исходную реализацию и позволяет указать 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);
    }
}

Затем вы можете использовать это в своем действии контроллера следующим образом:

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

Ответ 4

Основываясь на ответе Ричарда Гарсида, здесь версия ASP.Net Core

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

Затем в вашем контроллере вернитесь следующим образом:

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

Ответ 5

Вы должны вернуть объект ошибки JSON самостоятельно после установки StatusCode, например...

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

Другой способ - иметь JsonErrorModel и заполнить его

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
}

Посмотрите на ответ здесь, а также

Ответ 6

Вам нужно решить, хотите ли вы "HTTP-уровень ошибки" (для каких кодов ошибок) или "ошибка уровня приложения" (что для вашего пользовательского ответа JSON).

Большинство объектов высокого уровня, использующих HTTP, никогда не будут искать ответный поток, если код ошибки установлен на то, что не является 2xx (диапазон успеха). В вашем случае вы явно устанавливаете код ошибки для отказа (я думаю, 403 или 500) и вынуждают объект XMLHttp игнорировать тело ответа.

Исправить - либо обрабатывать условия ошибки на стороне клиента, либо не устанавливать код ошибки и возвращать JSON с информацией об ошибке (см. ответ Sbossb для деталей).

Ответ 7

И если ваши потребности не так сложны, как Сарат, вы можете уйти с чем-то еще более простым:

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

Ответ 8

То, что сработало для меня (и что я взял из fooobar.com/questions/147674/...), это установить флаг:

Response.TrySkipIisCustomErrors = true;

Ответ 9

Простой способ отправить сообщение об ошибке в Json - это управление Http Status Code объекта ответа и установка собственного сообщения об ошибке.

контроллер

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