38

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

public class NLogExceptionLogger : ExceptionLogger
{

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

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

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"
}

これで、Api Controller リクエスト パイプライン内で例外が発生した場合でも問題なく動作しています。

しかし、私の状況では、Microsoft.Owin.Security.OAuthベアラートークンを生成するためにミドルウェアを使用してValidateClientAuthenticationNLogExceptionLoggerます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 モデルをクライアントに返す必要があるため、例外の応答を変更する方法がまったくわかりません。ここに依存しようとした関連する回答がありますが、うまくいきませんでした。

これが私の Startup クラスと、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);
        }
    }
}
4

3 に答える 3

2

受け入れられた回答は不必要に複雑であり、OwinMiddlewareクラスから継承されません

あなたがする必要があるのはこれだけです:

 public class HttpLogger : OwinMiddleware
    {
        
        public HttpLogger(OwinMiddleware next) : base(next) { }

        public override async Task Invoke(IOwinContext context)
        {
            
            await Next.Invoke(context);
            Log(context)
            
        }
    }

また、拡張メソッドを作成する必要はありません..なしで参照するのに十分簡単です

 appBuilder.Use(typeof(HttpErrorLogger));

また、特定のリクエストのみをログに記録したい場合は、コンテキスト プロパティでフィルタリングできます。

元:

if (context.Response.StatusCode != 200) { Log(context) }
于 2021-07-14T16:15:20.513 に答える