2012-07-06 35 views
27

Estaba tratando de devolver un error a la llamada al controlador como se aconseja en This link 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í es el código de ejemploDevolver JSON con código de estado de error MVC

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

consigo el JSON si no se establece la statuscode. Si configuro el código de estado, obtengo el código de estado pero no el objeto de error Json.

Actualización Quiero enviar un objeto de error como JSON para que se pueda manejar la devolución de llamada de error de ajax.

Respuesta

26

he encontrado la solución here

tuve que crear un filtro de acción para anular el comportamiento por defecto de la 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 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 la acción, voy a 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 el error es lanzado el filtro de acción que implementa IExceptionFilter ser llamado y Devuelvo el Json al cliente en caso de devolución de llamada por error.

+10

En caso de que el lector piense "¿Es todo lo necesario?", La respuesta es "no". - Estaba en la misma situación que @Sarath y quería devolver un código de error HTTP y algunos datos JSON que describían el error. Resultó que solo podía usar las líneas donde se borró la respuesta, los errores personalizados de IIS se omiten y el código de estado es sat. Puse esas 3 líneas en mi Acción en mi Controlador, y después de esas 3 líneas, simplemente devolví mis datos JSON como siempre. Trabajado como un encanto. –

+6

De hecho, es cierto, pero para ser reutilizable, debería hacerlo como indica la respuesta en lugar de copiar/pasar el mismo código en cada acción. – Hades

+0

Si configuro 'StatusCode = 500', entonces ignora mi JsonResponse y en su lugar devuelve" La página no se puede mostrar porque se ha producido un error interno del servidor ". No estoy seguro si esto se debe a una diferencia en la tubería OWIN o qué. – AaronLS

4

usted tiene que volver JSON objeto de error a sí mismo después de ajustar el StatusCode, al igual 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 rellenarla

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 
} 

Ven a ver la respuesta here así como

+4

Ya tengo el error en el objeto de respuesta. El problema es que recibo "Solicitud incorrecta" y no el objeto JSON. Si no configuro el estado, obtengo el JSON con errores pero el cliente no sabe que es una condición de excepción. – Sarath

+0

@Sarath Compruebe el enlace en la respuesta. Necesita usar el atributo de error del método ajax en JQuery –

+1

. El problema al que me enfrento es que en la respuesta no estoy recuperando el JSON si configuro el estado. La respuesta que apuntó es exactamente qué estoy haciendo. El problema es que si establezco el estado de respuesta no obtengo el JSON. – Sarath

4

Debe decidir si desea "error de nivel HTTP" (para qué son los códigos de error) o "error de nivel de aplicación" (que 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 corregir: 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).

+1

Gracias por su respuesta. ¿Cómo sabrá el cliente que hubo una excepción si no configuré el código de estado? – Sarath

+0

@Sarath, la misma verificación 'response.errors && response.errors.length> 0' como lo está haciendo ahora en el lado del servidor (suponiendo que el cliente de JavaScript). –

+2

@Alexi Sí. Pero me gustaría informar implícitamente al cliente que se ha producido un error. Es decir, me gustaría manejar esta condición en la devolución de llamada fallida de la llamada ajax en lugar de la devolución de llamada exitosa y luego buscar errors.length. – Sarath

17

Existe una solución muy elegante a este problema, simplemente configure su sitio a través de la web.config:

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

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

+1

BOOM! La respuesta que estaba buscando. Funcionó bien para mí a nivel local, pero no en el servidor remoto. Sabía que se podría hacer con algunas configuraciones de configuración. ¡Aclamaciones! – ThiagoPXP

+0

¡Pruébalo si funciona en local pero no en azul! – Icycool

+0

Porque el problema era que estaba obteniendo StatusCode y Json Response en la máquina local (dev), pero mientras lo ejecutaba desde la producción recibía solo el StatusCode. Esta solución es, de lejos, la más fácil. – Vikneshwar

2

Y si sus necesidades no son tan complejos como Sarath de que puede salirse con 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 }; 
     } 
    } 
} 
6

La solución más elegante que he encontrado es crear su propio JsonResult que extiende la implementación original y le permite 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); 
    } 
} 

A continuación, puede utilizar esto en su acción de controlador de este modo:

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

Sobre la base de la respuesta de Richard Garside, aquí está la versión Core ASP.Net

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, de regreso si lo sigue:

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

Gracias por esto. – aman

Cuestiones relacionadas