微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

asp.net-web-api – 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,"Details": "Ooops!","ErrorReference": "56627a45d23732d2","DateTime": "2015-12-27T09:42:40.2982314Z"
}

现在,如果在Api Controller请求管道中发生任何异常,这一切都会很好。

但是在我的情况下,我正在使用中间件Microsoft.Owin.Security.OAuth来生成承载令牌,而这个中间件并不知道有关Web API异常处理的任何内容,所以例如如果异常已经被抛出在ValidateClientAuthentication方法中NLogExceptionLogger没有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中间商产生的异常?我跟着this answer,创建了一个异常处理目的的中间件,并将其注册为第一个,并且我能够执行源自“OAuthAuthorizationServerProvider”的日志异常,但是我不知道这是否是最佳的方法

2 – 现在当我在上一步中实现日志记录时,我真的不知道如何更改异常的响应,因为在“OAuthAuthorizationServerProvider”中发生异常时,我需要向客户端返回一个标准的JSON模型。有一个相关的answer here我试图依赖,但它没有工作。

这是我的启动类和我为异常捕获/记录创建的自定义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);
        }
    }
}

解决方法

好的,所以这比预期更容易,谢谢@Khalid的头脑,我最终创建了一个名为OwinExceptionHandlerMiddleware的Owin中间件,专门用于处理任何Owin中间件中发生的任何异常(记录它并在返回之前操纵响应)给客户)。

您需要将这个中间件注册为Startup类中的第一个,如下所示:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var httpConfig = new HttpConfiguration();

        httpConfig.MapHttpAttributeRoutes();

        httpConfig.Services.Replace(typeof(IExceptionHandler),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'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>();
    }
}

原文地址:https://www.jb51.cc/aspnet/252116.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐