Подтвердить что ты не робот

Глобальная обработка исключений в промежуточном ПО OWIN

Я пытаюсь создать единую обработку ошибок/отчетность в ASP.NET Web API 2.1 Проект, построенный поверх промежуточного ПО OWIN (IIS HOST с использованием Owin.Host.SystemWeb). В настоящее время я использовал собственный журнал регистрации исключений, который наследует от 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"
}

Теперь это работает отлично, если какое-либо исключение происходит w в конвейере запроса контроллера Api.

Но в моей ситуации я использую промежуточное ПО Microsoft.Owin.Security.OAuth для создания токенов-носителей, и это промежуточное программное обеспечение ничего не знает о обработке исключений веб-API, например, если в методе ValidateClientAuthentication было выбрано исключение < мой 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", mt snot уверен, что это оптимальный способ сделать это.

2 - Теперь, когда я выполнил ведение журнала как на предыдущем шаге, я действительно не знаю, как изменить ответ исключения, поскольку мне нужно вернуть клиенту стандартную модель JSON для любого исключения, происходящего в "OAuthAuthorizationServerProvider". Существует связанный ответ здесь. Я пытался зависеть, но это не сработало.

Вот мой класс 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);
        }
    }
}
4b9b3361

Ответ 1

Итак, это было проще, чем ожидалось, спасибо за @Khalid за хедз-ап, я закончил создание промежуточного программного обеспечения owin с именем OwinExceptionHandlerMiddleware, которое предназначено для обработки любых исключений, происходящих в любом промежуточном программном обеспечении Owin (протоколирование и управление ответ, прежде чем вернуть его клиенту).

Вам необходимо зарегистрировать это промежуточное ПО как первое в классе Startup как показано ниже:

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

И код, используемый в OwinExceptionHandlerMiddleware, как показано ниже:

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

Ответ 2

Есть несколько способов сделать то, что вы хотите:

  • Создайте промежуточное программное обеспечение, которое зарегистрировано первым, тогда все исключения перейдут на это промежуточное программное обеспечение. На этом этапе просто выпишите свой JSON через объект Response через контекст OWIN.

  • Вы также можете создать промежуточное ПО для переноса, которое обертывает промежуточное ПО Oauth. В этом случае он будет записывать ошибки, возникающие из этого конкретного кода.

В конечном счете, написать сообщение JSON о его создании, сериализации и записи его в ответ через контекст OWIN.

Кажется, что вы на правильном пути с №1. Надеюсь, это поможет, и удачи:)