我正在尝试在构建于 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"
}
现在,如果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 中间件生成的异常的全局异常处理程序?我按照这个答案创建了一个用于异常处理的中间件并将其注册为第一个中间件,并且我能够记录源自“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);
}
}
}