我试图在构建在ASP.NET中间件(使用Owin.Host.SystemWeb的IIS主机)之上的IISWebAPI2.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异常处理一无所知,因此,例如,如果在ValidateClientAuthentication my 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);
}
}因此,我希望就以下两个问题提供任何指导:
1-创建一个全局异常处理程序,只处理由OWIN中间设备生成的异常?我遵循this answer并创建了一个用于异常处理的中间件,并将其注册为第一个中间件,并且能够记录来自"OAuthAuthorizationServerProvider“的异常,但我不确定这是否是最佳的方法。
2-现在,当我在前面的步骤中实现日志记录时,我真的不知道如何更改异常的响应,因为我需要向客户端返回一个标准的JSON模型,用于处理在"OAuthAuthorizationServerProvider“中发生的任何异常。有一个相关的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);
}
}
}发布于 2015-12-28 11:53:46
好的,这比预期的要容易得多,谢谢@Khalid提醒,我最终创建了一个名为OwinExceptionHandlerMiddleware的owin中间件,它专门用于处理任何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'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>();
}
}发布于 2015-12-27 15:42:24
有几种方法可以做你想做的事:
编写JSON消息的最终目的是创建JSON消息,将其序列化,并通过OWIN上下文将其写入响应。
似乎你在正确的道路上与#1。希望这有帮助,并祝你好运:)
发布于 2021-07-14 16:15:20
接受的答案不必要地复杂,不能从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) }https://stackoverflow.com/questions/34479465
复制相似问题