asp.net-web-api asp.net-web-api2 owin katana owin-middleware

asp.net web api - Manejo global de excepciones en middleware OWIN



owin wikipedia (2)

Hay algunas maneras de hacer lo que desea:

  1. Cree el middleware que se registra primero , luego todas las excepciones aparecerán en ese middleware. En este punto, simplemente escriba su JSON a través del objeto Response a través del contexto OWIN.

  2. También puede crear un middleware de envoltura que envuelva el middleware de Oauth. En este caso, se aplicará a los errores de captura que se originen en esta ruta de código específica.

En última instancia, escribir su mensaje JSON se trata de crearlo, serializarlo y escribirlo en la Respuesta a través del contexto OWIN.

Parece que estás en el camino correcto con el # 1. Espero que esto ayude y buena suerte :)

Intento crear un manejo / informe de errores unificados en el proyecto ASP.NET Web API 2.1 construido sobre el middleware OWIN (IIS HOST usando Owin.Host.SystemWeb). Actualmente utilicé un registrador de excepciones personalizado que hereda de System.Web.Http.ExceptionHandling.ExceptionLogger y usa NLog para registrar todas las excepciones como el siguiente código:

public class NLogExceptionLogger : ExceptionLogger { private static readonly Logger Nlog = LogManager.GetCurrentClassLogger(); public override void Log(ExceptionLoggerContext context) { //Log using NLog } }

Deseo cambiar el cuerpo de la respuesta para todas las excepciones API a una respuesta unificada amigable que oculta todos los detalles de excepción utilizando System.Web.Http.ExceptionHandling.ExceptionHandler como el siguiente código:

public class ContentNegotiatedExceptionHandler : ExceptionHandler { public override void Handle(ExceptionHandlerContext context) { var errorDataModel = new ErrorDataModel { Message = "Internal server error occurred, error has been reported!", Details = context.Exception.Message, ErrorReference = context.Exception.Data["ErrorReference"] != null ? context.Exception.Data["ErrorReference"].ToString() : string.Empty, DateTime = DateTime.UtcNow }; var response = context.Request.CreateResponse(HttpStatusCode.InternalServerError, errorDataModel); context.Result = new ResponseMessageResult(response); } }

Y esto devolverá la respuesta a continuación para el cliente cuando ocurre una excepción:

{ "Message": "Internal server error occurred, error has been reported!", "Details": "Ooops!", "ErrorReference": "56627a45d23732d2", "DateTime": "2015-12-27T09:42:40.2982314Z" }

Ahora esto está funcionando todo bien si se produce alguna excepción dentro de una interconexión de solicitud de controlador Api .

Pero en mi situación estoy usando el middleware Microsoft.Owin.Security.OAuth para generar tokens de portador, y este middleware no sabe nada sobre el manejo de excepciones de la API web, así que, por ejemplo, si se ha lanzado una excepción en el método ValidateClientAuthentication my NLogExceptionLogger not ContentNegotiatedExceptionHandler sabrá algo sobre esta excepción ni tratará de manejarla, el código de muestra que utilicé en AuthorizationServerProvider es el siguiente:

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider { public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { //Expcetion occurred here int x = int.Parse(""); context.Validated(); return Task.FromResult<object>(null); } public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { if (context.UserName != context.Password) { context.SetError("invalid_credentials", "The user name or password is incorrect."); return; } var identity = new ClaimsIdentity(context.Options.AuthenticationType); identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); context.Validated(identity); } }

Por lo tanto, agradeceré cualquier orientación para implementar los siguientes 2 problemas:

1 - ¿Cree un controlador de excepción global que maneje solo las excepciones generadas por middleware OWIN ? Seguí esta respuesta y creé un middleware para el manejo de excepciones y lo registré como el primero y pude hacer excepciones de registro originadas de "OAuthAuthorizationServerProvider", pero no estoy seguro si esta es la forma óptima de hacerlo.

2 - Ahora cuando implementé el registro como en el paso anterior, realmente no tengo idea de cómo cambiar la respuesta de la excepción, ya que necesito devolver al cliente un modelo JSON estándar para cualquier excepción que ocurra en el "OAuthAuthorizationServerProvider". Aquí hay una respuesta relacionada en la que intenté depender, pero no funcionó.

Aquí está mi clase de inicio y el GlobalExceptionMiddleware personalizado que creé para la captura / registro de excepciones. La paz que falta es devolver una respuesta JSON unificada para cualquier excepción. Cualquier idea será apreciada.

public class Startup { public void Configuration(IAppBuilder app) { var httpConfig = new HttpConfiguration(); httpConfig.MapHttpAttributeRoutes(); httpConfig.Services.Replace(typeof(IExceptionHandler), new ContentNegotiatedExceptionHandler()); httpConfig.Services.Add(typeof(IExceptionLogger), new NLogExceptionLogger()); OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() { AllowInsecureHttp = true, TokenEndpointPath = new PathString("/token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), Provider = new AuthorizationServerProvider() }; app.Use<GlobalExceptionMiddleware>(); app.UseOAuthAuthorizationServer(OAuthServerOptions); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); app.UseWebApi(httpConfig); } } public class GlobalExceptionMiddleware : OwinMiddleware { public GlobalExceptionMiddleware(OwinMiddleware next) : base(next) { } public override async Task Invoke(IOwinContext context) { try { await Next.Invoke(context); } catch (Exception ex) { NLogLogger.LogError(ex, context); } } }


Ok, entonces esto fue más fácil de lo esperado, gracias por @Khalid por el aviso, he terminado creando un middleware llamado OwinExceptionHandlerMiddleware que está dedicado a manejar cualquier excepción que ocurra en cualquier Owin Middleware (registrando y manipulando la respuesta antes de devolverla) al cliente).

Debe registrar este middleware como el primero en la clase de Startup como se muestra a continuación:

public class Startup { public void Configuration(IAppBuilder app) { var httpConfig = new HttpConfiguration(); httpConfig.MapHttpAttributeRoutes(); httpConfig.Services.Replace(typeof(IExceptionHandler), new ContentNegotiatedExceptionHandler()); httpConfig.Services.Add(typeof(IExceptionLogger), new NLogExceptionLogger()); OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() { AllowInsecureHttp = true, TokenEndpointPath = new PathString("/token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), Provider = new AuthorizationServerProvider() }; //Should be the first handler to handle any exception happening in OWIN middlewares app.UseOwinExceptionHandler(); // Token Generation app.UseOAuthAuthorizationServer(OAuthServerOptions); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); app.UseWebApi(httpConfig); } }

Y el código utilizado en el OwinExceptionHandlerMiddleware como a continuación:

using AppFunc = Func<IDictionary<string, object>, Task>; public class OwinExceptionHandlerMiddleware { private readonly AppFunc _next; public OwinExceptionHandlerMiddleware(AppFunc next) { if (next == null) { throw new ArgumentNullException("next"); } _next = next; } public async Task Invoke(IDictionary<string, object> environment) { try { await _next(environment); } catch (Exception ex) { try { var owinContext = new OwinContext(environment); NLogLogger.LogError(ex, owinContext); HandleException(ex, owinContext); return; } catch (Exception) { // If there''s a Exception while generating the error page, re-throw the original exception. } throw; } } private void HandleException(Exception ex, IOwinContext context) { var request = context.Request; //Build a model to represet the error for the client var errorDataModel = NLogLogger.BuildErrorDataModel(ex); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; context.Response.ReasonPhrase = "Internal Server Error"; context.Response.ContentType = "application/json"; context.Response.Write(JsonConvert.SerializeObject(errorDataModel)); } } public static class OwinExceptionHandlerMiddlewareAppBuilderExtensions { public static void UseOwinExceptionHandler(this IAppBuilder app) { app.Use<OwinExceptionHandlerMiddleware>(); } }