58

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

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

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

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

4

13 回答 13

42

我在这里找到了解决方案

我必须创建一个动作过滤器来覆盖 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。

于 2012-07-12T00:36:57.583 回答
42

我发现的最巧妙的解决方案是创建自己的 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);
}
于 2017-03-28T12:41:55.907 回答
30

这个问题有一个非常优雅的解决方案,只需通过 web.config 配置您的站点:

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

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

于 2013-06-20T08:20:59.977 回答
10

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

然后在你的控制器中,返回如下:

// 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);
于 2017-05-04T13:38:37.340 回答
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
        }
  });
于 2019-04-09T16:34:29.597 回答
7

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

Response.TrySkipIisCustomErrors = true;
于 2018-11-26T10:27:40.783 回答
5

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

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

也看看这里的答案

于 2012-07-06T22:38:22.930 回答
4

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

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

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

于 2012-07-06T22:41:48.003 回答
3

如果您的需求不像 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 };
      }
   }
}
于 2013-08-04T17:43:09.040 回答
3

一些响应依赖于抛出的异常并在 OnException 覆盖中对其进行处理。就我而言,如果用户传入了错误的 ID,我想返回诸如错误请求之类的状态。对我有用的是使用ControllerContext:

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

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

return jsonResult;
于 2020-03-27T11:58:31.577 回答
2

如果您只是使用 MVC,最简单的方法是使用 HttpStatusCodeResult。

public ActionResult MyAjaxRequest(string args)
    {
        string error_message = string.Empty;
        try
        {
            // successful
            return Json(args);
        }
        catch (Exception e)
        {
            error_message = e.Message;
        }

        return new HttpStatusCodeResult(500, error_message);
    }

当错误返回给客户端时,您可以根据需要显示或操作它。

request.fail(function (jqXHR) {
        if (jqXHR.status == 500) {
            alert(jqXHR.statusText);
        }
    })
于 2021-09-14T01:37:53.553 回答
1

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

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

按照 Richard 的示例,您可以像这样使用此类:

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

不幸的是,您不能对内容使用匿名类型,因为您需要将具体类型(例如:)传递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());
    }
}

然后你可以将它与这样的匿名类型一起使用:

if(thereWereErrors)
{
    var errorModel = new { error = "There was an error" };
    return Json(errorModel, HttpStatusCode.InternalServerError);
}
于 2019-12-11T09:56:37.450 回答
0

这是 ASP.NET v5+ 的 JsonResult 覆盖答案。我已经测试过,它的工作原理和早期版本一样好。

public class JsonHttpStatusResult : JsonResult
{
    private readonly HttpStatusCode _httpStatus;

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

    public override Task ExecuteResultAsync(ActionContext context)
    {
        context.HttpContext.Response.StatusCode = (int)_httpStatus;
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var services = context.HttpContext.RequestServices;
        var executor = services.GetRequiredService<IActionResultExecutor<JsonResult>>();
        return executor.ExecuteAsync(context, this);
    }
}
于 2021-12-02T21:03:25.257 回答