[英]Return JSON with error status code MVC
我試圖按照此鏈接中的建議向控制器的調用返回錯誤,以便客戶端可以采取適當的操作。 控制器由 javascript 通過 jquery AJAX 調用。 只有當我沒有將狀態設置為錯誤時,我才會返回 Json 對象。 這是示例代碼
if (response.errors.Length > 0)
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(response);
如果我不設置狀態碼,我會得到 Json。 如果我設置狀態代碼,我會返回狀態代碼,但不會返回 Json 錯誤對象。
更新我想將一個 Error 對象作為 JSON 發送,以便它可以處理 ajax 的錯誤回調。
我在這里找到了解決方案
我必須創建一個動作過濾器來覆蓋 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。
我發現的最簡潔的解決方案是創建您自己的 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);
}
這個問題有一個非常優雅的解決方案,只需通過 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
基於 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);
一種向 Json 發送錯誤的簡單方法是控制響應對象的 Http 狀態碼並設置自定義錯誤消息。
控制器
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
}
});
設置 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
}
看看這里的答案
對我有用的東西(我從另一個 stackoverflow 響應中獲取的)是設置標志:
Response.TrySkipIisCustomErrors = true;
您需要決定是否需要“HTTP 級別錯誤”(錯誤代碼用於什么)或“應用程序級別錯誤”(您的自定義 JSON 響應用於什么)。
如果錯誤代碼設置為非 2xx(成功范圍),大多數使用 HTTP 的高級對象永遠不會查看響應流。 在您的情況下,您明確將錯誤代碼設置為失敗(我認為是 403 或 500)並強制 XMLHttp 對象忽略響應正文。
修復 - 在客戶端處理錯誤條件或不設置錯誤代碼並返回帶有錯誤信息的 JSON(有關詳細信息,請參閱 Sbossb 回復)。
如果您的需求不像 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 };
}
}
}
一些響應依賴於拋出的異常並在 OnException 覆蓋中處理它。 就我而言,如果用戶傳入了錯誤的 ID,我想返回錯誤請求等狀態。 對我有用的是使用 ControllerContext:
var jsonResult = new JsonResult { JsonRequestBehavior = JsonRequestBehavior.AllowGet, Data = "whoops" };
ControllerContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return jsonResult;
我正在運行 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;
}
}
按照理查德的例子,你可以像這樣使用這個類:
if(thereWereErrors)
{
var errorModel = new CustomErrorModel("There was an error");
return new JsonHttpStatusResult<CustomErrorModel>(errorModel, new JsonSerializerSettings(), new UTF8Encoding(), this, HttpStatusCode.InternalServerError);
}
不幸的是,您不能對內容使用匿名類型,因為您需要將具體類型(例如: CustomErrorType
)傳遞給JsonHttpStatusResult
初始值設定項。 如果您想使用匿名類型,或者你只是想成為真正的華而不實,您可以通過繼承建立在這種解決方案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);
}
如果您只是使用 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);
}
})
這是 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);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.