C# 返回带有错误状态代码 MVC 的 JSON

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11370251/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 17:37:45  来源:igfitidea点击:

Return JSON with error status code MVC

c#asp.net-mvcjson

提问by Sarath

I was trying to return an error to the call to the controller as advised in This linkso 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

我试图按照此链接中的建议向控制器的调用返回错误, 以便客户端可以采取适当的操作。控制器由 javascript 通过 jquery AJAX 调用。只有当我没有将状态设置为错误时,我才会返回 Json 对象。这是示例代码

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.

如果我不设置状态码,我会得到 Json。如果我设置状态代码,我会返回状态代码,但不会返回 Json 错误对象。

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

更新我想将一个 Error 对象作为 JSON 发送,以便它可以处理 ajax 的错误回调。

采纳答案by Sarath

I found the solution here

我在这里找到了解决方案

I had to create a action filter to override the default behaviour of MVC

我必须创建一个动作过滤器来覆盖 MVC 的默认行为

Here is my exception class

这是我的异常类

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

Note that I have constructor which initializes my JSON. Here is the action filter

请注意,我有初始化我的 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;
        }
    }

Now that I have the action filter, I will decorate my controller with the filter attribute

现在我有了动作过滤器,我将用过滤器属性装饰我的控制器

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

When the error is thrown the action filter which implements IExceptionFilter get called and I get back the Json on the client on error callback.

当错误被抛出时,实现 IExceptionFilter 的动作过滤器被调用,我在错误回调的客户端上取回 Json。

回答by Stefan Bossbaly

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

设置 StatusCode 后,您必须自己返回 JSON 错误对象,就像这样......

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 JsonErrorModeland populate it

另一种方法是拥有一个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
}

Take a look at the answer hereas well

看看答案在这里,以及

回答by Alexei Levenkov

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

您需要决定是否需要“HTTP 级别错误”(错误代码用于什么)或“应用程序级别错误”(您的自定义 JSON 响应用于什么)。

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.

如果错误代码设置为非 2xx(成功范围),大多数使用 HTTP 的高级对象永远不会查看响应流。在您的情况下,您明确将错误代码设置为失败(我认为是 403 或 500)并强制 XMLHttp 对象忽略响应正文。

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

修复 - 在客户端处理错误条件或不设置错误代码并返回带有错误信息的 JSON(有关详细信息,请参阅 Sbossb 回复)。

回答by Gabrielius

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

这个问题有一个非常优雅的解决方案,只需通过 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

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

回答by Brian

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

如果您的需求不像 Sarath 的那么复杂,您可以使用更简单的方法:

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

回答by Richard Garside

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

我发现的最简洁的解决方案是创建您自己的 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);
    }
}

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

回答by Lee Oades

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

基于 Richard Garside 的回答,这里是 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);
    }
}

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

回答by Philippe

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

对我有用的东西(我从另一个 stackoverflow 响应中获取的)是设置标志:

Response.TrySkipIisCustomErrors = true;

回答by Alexandre Cavaloti

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

一种向 Json 发送错误的简单方法是控制响应对象的 Http 状态码并设置自定义错误消息。

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

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

回答by mogelbuster

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:

我正在运行 Asp.Net Web Api 5.2.7,看起来 JsonResult 类已更改为使用泛型和异步执行方法。我最终改变了理查德加赛德的解决方案

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 JsonHttpStatusResultinitializer. If you want to use anonymous types, or you just want to be really slick, you can build on this solution by subclassing ApiControllerto add an HttpStatusCodeparam to the Jsonmethods :)

不幸的是,您不能对内容使用匿名类型,因为您需要将具体类型(例如:)传递CustomErrorTypeJsonHttpStatusResult初始值设定项。如果你想使用匿名类型,或者你只是想变得非常灵活,你可以通过子类化ApiController来构建这个解决方案,向方法添加一个HttpStatusCode参数Json:)

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