2012-08-08 9 views
13

Tengo un ExceptionFilter mundial en mi MVC \ WebAPI aplicación registrada como:Cómo devolver gracia excepción de un Handler WebAPI mensaje

public virtual void RegisterHttpFilters(HttpConfiguration config) 
{ 
    config.Filters.Add(new MyExceptionFilter(_exceptionHandler)); 
} 

donde MyExceptionFilter es:

public class MyExceptionFilter : ExceptionFilterAttribute 
{ 
    private readonly IMyExceptionHandler m_exceptionHandler; 

    public MyExceptionFilter(IMyExceptionHandler exceptionHandler) 
    { 
     m_exceptionHandler = exceptionHandler; 
    } 

    public override void OnException(HttpActionExecutedContext context) 
    { 
     Exception ex = context.Exception; 
     if (ex != null) 
     { 
      object response = null; 
      HttpStatusCode statusCode = m_exceptionHandler != null 
       ? m_exceptionHandler.HandleException(ex, out response) 
       : HttpStatusCode.InternalServerError; 
      context.Response = context.Request.CreateResponse(statusCode, response ?? ex); 
     } 

     base.OnException(context); 
    } 
} 

Este filtro devuelve todas las excepciones como json-objects y permite que alguna IMyExceptionHandler-implementation personalice el objeto que se devuelve.

Todo esto funciona bien. Hasta que tengo una excepción en algunos de mis controladores de mensajes:

public class FooMessageHandler : DelegatingHandler 
{ 
    private readonly Func<IBar> _barFactory; 
    public FooMessageHandler(Func<IBar> barFactory) 
    { 
     _barFactory = varFactory; 
    } 

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     request.Properties["MY_BAR"] = _barFactory(); 
     return base.SendAsync(request, cancellationToken); 
    } 
} 

Como se puede ver este controlador crea un cierto componente y ponerlo en el mensaje de solicitud HTTP actual. Cuando ocurre una excepción en FuncOfIBar, obtengo la pantalla amarilla de la muerte. Mi ExceptionFilter no es llamado.

Traté de coger específicamente la excepción en el controlador de mensajes y volver HttpResponseException pero no cambia nada - que todavía consigue YSOD:

public class XApplicationInitializerMessageHandler : DelegatingHandler 
{ 
    private readonly Func<IXCoreApplication> _appFactory; 
    private readonly IXExceptionHandler m_exceptionHandler; 

    public XApplicationInitializerMessageHandler(Func<IXCoreApplication> appFactory, IXExceptionHandler exceptionHandler) 
    { 
     ArgumentValidator.EnsureArgumentNotNull(appFactory, "appFactory"); 
     _appFactory = appFactory; 
     m_exceptionHandler = exceptionHandler; 
    } 

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     try 
     { 
      request.SetApplication(_appFactory()); 
     } 
     catch (Exception ex) 
     { 
      object resultObject = null; 
      HttpStatusCode statusCode = m_exceptionHandler != null 
       ? m_exceptionHandler.HandleException(ex, out resultObject) 
       : HttpStatusCode.InternalServerError; 
      HttpResponseMessage responseMessage = request.CreateResponse(statusCode, resultObject ?? ex); 

      throw new HttpResponseException(responseMessage); 
     } 
     return base.SendAsync(request, cancellationToken); 
    } 
} 

}

Quiero comportamiento de mi aplicación para ser igual independientemente de dónde ocurre una excepción: en un ApiController o un manejador de mensajes.

¿Cómo hacer esto?

Conozco Application_Error pero me gustaría mantener la personalización de HttpApplication intocable.

+0

http://stackoverflow.com/questions/13013973/asp-net-web-api-default-error-messages – Timeless

Respuesta

8

En lugar de tirar HttpResponseException vez debería devolver un HttpResponseMessage:

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
{ 
    try 
    { 
     request.SetApplication(_appFactory()); 
    } 
    catch (Exception ex) 
    { 
     object resultObject = null; 
     HttpStatusCode statusCode = m_exceptionHandler != null 
      ? m_exceptionHandler.HandleException(ex, out resultObject) 
      : HttpStatusCode.InternalServerError; 
     HttpResponseMessage responseMessage = request.CreateResponse(statusCode, resultObject ?? ex); 

     return Task<HttpResponseMessage>.Factory.StartNew(() => responseMessage); 
    } 
    return base.SendAsync(request, cancellationToken); 
} 
5

Lo que he hecho con éxito es el uso de un enfoque basado en atributos. Se coloca un atributo personalizado en cada una de las acciones que deben utilizados manejo de excepciones:

[ExceptionHandling] 
public IEnumerable<string> Get() 

Su atributo personalizado manejo de excepciones se lleva a cabo de esta manera:

public class ExceptionHandlingAttribute : ExceptionFilterAttribute 
{ 
    public override void OnException(HttpActionExecutedContext context) 
    {} 

Como se puede ver, el atributo personalizado es heredado de ExceptionFilterAttribute. A continuación, solo anula el método OnException. Lo que entonces puede hacer es observar el tipo de excepción y manejarla de acuerdo con las reglas comerciales o lo que se ajuste a sus necesidades. En algunos casos, puede tragar la excepción en el servidor. Si desea notificar al cliente, puede lanzar una HttpResponseException, que puede contener toda la información relevante, como mensaje, pila de llamadas, excepción interna, etc.

Para obtener más inspiración, eche un vistazo a esta gran publicación de blog: ASP.NET Web API Exception Handling

5

Tuve el mismo problema con los filtros de excepciones globales que no se están ejecutando. La solución fue que los filtros de excepción de la API web deben configurarse en una colección global diferente a los filtros de excepción ASP.NET MVC.

Para configurar ASP.Manejo de Red de error MVC debe ejecutar:

System.Web.Mvc.GlobalFilters.Filters.Add(new MyMvcExceptionFilter()); 

Pero para el error de la API Web manejo debe ejecutar:

System.Web.Http.GlobalConfiguration.Configuration.Filters.Add(new MyWebApiExceptionFilter()); 

Desde mi experiencia limitada con la API de Web este tipo de situación es típica: superficialmente los patrones utilizados será muy similar a ASP.NET MVC y tan instantáneamente familiar. Esto da la ilusión de que escribir un fragmento de código tendrá un efecto en ambos frameworks, pero en realidad sus implementaciones debajo de él están en gran parte o completamente separadas y tendrás que escribir dos versiones del código que sean muy similares para hacerlo todo trabajo.

excelente referencia sobre el manejo de excepciones API Web: http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling

2

En startup.cs:

config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler()); 

he encontrado que el uso de GlobalExceptionHandler puede detectar la excepción en la delegación de manipulador y devolver objetos JSON personalizados.

0

encontré este blog con muy buen ejemplo: http://nodogmablog.bryanhogan.net/2016/07/getting-web-api-exception-details-from-a-httpresponsemessage/

adopté el código con algunas actualizaciones:

if ((int)response.StatusCode >= 400) 
{ 
     exceptionResponse = JsonConvert.DeserializeObject<ExceptionResponse>(LogRequisicao.CorpoResposta); 
     LogRequisicao.CorpoResposta = exceptionResponse.ToString() ; 
     if (exceptionResponse.InnerException != null) 
      LogRequisicao.CorpoResposta += "\r\n InnerException: " + exceptionResponse.ToString(); 
} 

usando el objeto:

public class ExceptionResponse 
    { 
     public string Message { get; set; } 
     public string ExceptionMessage { get; set; } 
     public string ExceptionType { get; set; } 
     public string StackTrace { get; set; } 
     public ExceptionResponse InnerException { get; set; } 

     public override String ToString() 
     { 
      return "Message: " + Message + "\r\n " 
       + "ExceptionMessage: " + ExceptionMessage + "\r\n " 
       + "ExceptionType: " + ExceptionType + " \r\n " 
       + "StackTrace: " + StackTrace + " \r\n ";   
     } 
    } 
Cuestiones relacionadas