web-dev-qa-db-ja.com

OWINミドルウェアでのグローバル例外処理

OWINミドルウェア(Owin.Host.SystemWebを使用するIISホスト)上に構築されたASP.NET Web API 2.1プロジェクトで、統一されたエラー処理/レポートを作成しようとしています。現在、System.Web.Http.ExceptionHandling.ExceptionLoggerを継承し、NLogを使用してすべての例外を以下のコードとして記録するカスタム例外ロガーを使用しました。

public class NLogExceptionLogger : ExceptionLogger
{

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

すべてのAPI例外の応答本文を、以下のコードとしてSystem.Web.Http.ExceptionHandling.ExceptionHandlerを使用してすべての例外の詳細を非表示にするわかりやすい統合応答に変更したい:

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);
    }
}

そして、これは、例外が発生したときにクライアントに対して以下の応答を返します。

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

これは、例外が発生した場合にすべてうまく機能します。wApi Controller request pipeline

しかし、私の状況では、ベアラートークンの生成にミドルウェアMicrosoft.Owin.Security.OAuthを使用しており、このミドルウェアはWeb API例外処理について何も知らないため、たとえば、メソッドValidateClientAuthentication my NLogExceptionLogger notで例外がスローされた場合ContentNegotiatedExceptionHandlerはこの例外について何も知らず、それを処理しようとしません。AuthorizationServerProviderで使用したサンプルコードは次のとおりです。

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);
    }
}

したがって、以下の2つの問題を実装する際のガイダンスに感謝します。

1-OWINミドルウェアによって生成された例外のみを処理する?のグローバル例外ハンドラーを作成します この回答 に従い、例外処理用のミドルウェアを作成し、最初のミドルウェアとして登録し、「OAuthAuthorizationServerProvider」から発生した例外をログに記録することができましたが、これが最適な方法。

2-前の手順のようにロギングを実装したとき、「OAuthAuthorizationServerProvider」で発生する例外の標準JSONモデルをクライアントに返す必要があるため、例外の応答を変更する方法がまったくわかりません。関連する 答えはこちら に依存しようとしましたが、うまくいきませんでした。

これが、スタートアップクラスと、例外のキャッチ/ロギング用に作成したカスタムGlobalExceptionMiddlewareです。欠けている平和は、例外に対して統一されたJSON応答を返すことです。どんなアイデアでも大歓迎です。

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);
        }
    }
}
32
Taiseer Joudeh

あなたがしたいことをするいくつかの方法があります:

  1. 登録されているミドルウェアを作成しますfirst、すべての例外はそのミドルウェアにバブルアップします。この時点で、OWINコンテキストを介してResponseオブジェクトを介してJSONを書き出すだけです。

  2. Oauthミドルウェアをラップするラッピングミドルウェアを作成することもできます。この場合、この特定のコードパスから発生するエラーをキャプチャします。

最終的にJSONメッセージを作成するということは、OWINコンテキストを介してメッセージを作成し、シリアル化し、Responseに書き込むことです。

#1で正しい道を進んでいるようです。これがお役に立てば幸いです:)

7