我正在尝试在基于OWIN中间件(使用Owin.Host.SystemWeb的IIS HOST)构建的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);
}
}
希望能得到指导,解决以下两个问题:
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);
}
}
}