c# - jsonrequestbehavior - Devuelve JSON con el código de estado de error MVC
return json data to view mvc (7)
Estaba tratando de devolver un error a la llamada al controlador como se recomienda en este enlace para que el cliente pueda tomar las medidas adecuadas. El controlador es llamado por javascript a través de jquery AJAX. Recupero el objeto Json solo si no establezco el estado en error. Aquí está el código de ejemplo
if (response.errors.Length > 0)
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(response);
Obtengo el Json si no configuro el código de estado. Si configuro el código de estado, obtengo el código de estado pero no el objeto de error Json.
Actualización Deseo enviar un objeto Error como JSON para que se pueda manejar la devolución de llamada de error de ajax.
Basándose en la respuesta de Richard Garside, aquí está la versión 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);
}
}
Luego, en su controlador, regrese de la siguiente manera:
// 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);
Debe decidir si desea "error de nivel HTTP" (para qué sirven los códigos de error) o "error de nivel de aplicación" (para qué sirve su respuesta JSON personalizada).
La mayoría de los objetos de alto nivel que usan HTTP nunca buscarán en el flujo de respuesta si el código de error se establece en algo que no es 2xx (rango de éxito). En su caso, está configurando explícitamente un error en el código de error (creo que 403 o 500) y fuerza al objeto XMLHttp a ignorar el cuerpo de la respuesta.
Para solucionarlo: maneje las condiciones de error en el lado del cliente o no configure el código de error y devuelva JSON con información de error (consulte la respuesta de Sbossb para obtener más información).
Debe devolver el objeto de error JSON usted mismo después de configurar StatusCode, así que ...
if (BadRequest)
{
Dictionary<string, object> error = new Dictionary<string, object>();
error.Add("ErrorCode", -1);
error.Add("ErrorMessage", "Something really bad happened");
return Json(error);
}
Otra forma es tener un JsonErrorModel
y poblarlo
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
}
Echa un vistazo a la respuesta here también
Encontré la solución here
Tuve que crear un filtro de acción para anular el comportamiento predeterminado de MVC
Aquí está mi clase de excepción
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) { }
}
Tenga en cuenta que tengo un constructor que inicializa mi JSON. Aquí está el filtro de acción
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;
}
}
Ahora que tengo el filtro de acción, decoraré mi controlador con el atributo de filtro
[HandleUIException]
public JsonResult UpdateName(string objectToUpdate)
{
var response = myClient.ValidateObject(objectToUpdate);
if (response.errors.Length > 0)
throw new ValidationException(Json(response));
}
Cuando se lanza el error, se llama al filtro de acción que implementa IExceptionFilter y recupero el Json en el cliente en caso de devolución de llamada por error.
Hay una solución muy elegante a este problema, simplemente configure su sitio a través de web.config:
<system.webServer>
<httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough"/>
</system.webServer>
La mejor solución que he encontrado es crear tu propio JsonResult que extienda la implementación original y te permita especificar un 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);
}
}
Puede usar esto en su acción de controlador así:
if(thereWereErrors)
{
var errorModel = new { error = "There was an error" };
return new JsonHttpStatusResult(errorModel, HttpStatusCode.InternalServerError);
}
Y si sus necesidades no son tan complejas como las de Sarath, puede hacer algo aún más simple:
[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 };
}
}
}