Mondial de la gestion des exceptions dans OWIN middleware
j'essaie de créer un traitement/rapport d'erreur unifié dans ASP.NET Web API 2.1 projet construit sur le dessus D'OWIN middleware (IIS HOST using Owin.Hôte.SystemWeb).
Actuellement, j'ai utilisé un logger d'exception personnalisé qui hérite de System.Web.Http.ExceptionHandling.ExceptionLogger
et utilise NLog pour enregistrer toutes les exceptions comme le code ci-dessous:
public class NLogExceptionLogger : ExceptionLogger
{
private static readonly Logger Nlog = LogManager.GetCurrentClassLogger();
public override void Log(ExceptionLoggerContext context)
{
//Log using NLog
}
}
Le je veux changer le corps de la réponse pour toutes les API des exceptions à un sympathique réponse unifiée qui cache tous les détails de l'exception à l'aide de System.Web.Http.ExceptionHandling.ExceptionHandler
comme le code suivant:
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);
}
}
et ceci retournera la réponse ci-dessous pour le client quand une exception se produit:
{
"Message": "Internal server error occurred, error has been reported!",
"Details": "Ooops!",
"ErrorReference": "56627a45d23732d2",
"DateTime": "2015-12-27T09:42:40.2982314Z"
}
maintenant cela fonctionne très bien si n'importe quelle exception se produit w ithin an Api Controller request pipeline .
mais dans ma situation j'utilise le middleware Microsoft.Owin.Security.OAuth
pour générer des jetons au porteur, et ce middleware ne connaît rien à l'exception de L'API Web manipulation, donc par exemple si une exception a été lancée dans la méthode ValidateClientAuthentication
mon NLogExceptionLogger
pas ContentNegotiatedExceptionHandler
saura quoi que ce soit au sujet de cette exception ni essayer de la manipuler, le code d'échantillon que j'ai utilisé dans le AuthorizationServerProvider
est comme ci-dessous:
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);
}
}
donc, je vais apprécier toute orientation dans la mise en œuvre des 2 questions suivantes:
1-Créer un gestionnaire d'exception global qui ne gère que les exceptions générées par OWIN middle marchandises ? J'ai suivi cette réponse et j'ai créé un middleware pour le traitement des exceptions et je l'ai enregistré comme le premier et j'ai été capable de faire des exceptions log provenues de "OAuthAuthorizationServerProvider", mais je ne suis pas sûr que ce soit la meilleure façon de le faire.
2-Maintenant, quand j'ai mis en œuvre la journalisation comme l'étape précédente, je n'ai vraiment aucune idée comment changer la réponse de l'exception que je dois retourner à la client un modèle JSON standard pour toute exception survenant dans le"OAuthAuthorizationServerProvider". Il ya un réponse ici j'ai essayé de dépendre de, mais il n'a pas fonctionné.
voici ma classe de démarrage et la coutume GlobalExceptionMiddleware
que j'ai créée pour la capture d'exception/journalisation. La paix manquante renvoie une réponse JSON unifiée pour toute exception. Toutes les idées seront appréciées.
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);
}
}
}
2 réponses
Ok, donc c'était plus facile que prévu, merci pour @Khalid pour les mises en garde, j'ai fini par créer un middleware owin nommé OwinExceptionHandlerMiddleware
qui est dédié pour gérer toute exception se produisant dans un Middleware Owin (journalisation et manipulation de la réponse avant de la retourner au client).
vous devez enregistrer ce middleware comme le premier dans la classe Startup
comme ci-dessous:
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);
}
}
et le code utilisé dans le OwinExceptionHandlerMiddleware
comme suit:
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>();
}
}
il y a plusieurs façons de faire ce que vous voulez:
-
créer middleware qui est enregistré d'abord , puis toutes les exceptions va bulle jusqu'à cet middleware. À ce stade, vous n'avez qu'à écrire votre JSON via l'objet Response via le contexte OWIN.
-
vous pouvez également créer un middleware enveloppant qui enveloppe l'autre middleware. Dans ce cas, il sera sur les erreurs de capture provenant de ce chemin de code spécifique.
en fin de Compte, écrire votre message JSON, c'est le créer, le sérialiser, et l'écrire à la réponse via le contexte OWIN.
il semble que vous êtes sur la bonne voie avec le #1. Espérons que cela aide, et bonne chance :)