我正在尝试在构建于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
因此,我将理解实施以下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();
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);
}
}
}
Taiseer Joud.. 35
好的,所以这比预期的要容易,感谢@Khalid的抬头,我最终创建了一个名为owin的中间件OwinExceptionHandlerMiddleware
,专门用于处理任何Owin Middleware中发生的异常(记录它并在返回之前操纵响应)客户端).
您需要将此中间件注册为类中的第一个,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, 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 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();
}
}
事实证明,这是你必须自己写的东西.这里有一个很好的例子:https://github.com/filipw/apress-recipes-webapi/blob/master/Chapter%2007/7-2/Apress.Recipes.WebApi/Apress.Recipes.WebApi/ContentNegotiatedExceptionHandler. CS (3认同)
我想知道:你为什么停止继承"OwinMiddleware"?现在如何调用`Invoke()`方法,你不会继承`OwinExceptionHandlerMiddleware`中的任何东西? (3认同)
Khalid Abuha.. 8
有几种方法可以做你想要的:
创建首先注册的中间件,然后所有异常将冒泡到该中间件.此时,只需通过OWIN上下文通过Response对象写出您的JSON.
您还可以创建包装Oauth中间件的包装中间件.在这种情况下,它将捕获源自此特定代码路径的捕获错误.
最终编写JSON消息是关于创建它,序列化它,以及通过OWIN上下文将它写入Response.
看起来你在#1的正确道路上.希望这有帮助,祝你好运:)
好的,所以这比预期的要容易,感谢@Khalid的抬头,我最终创建了一个名为owin的中间件OwinExceptionHandlerMiddleware
,专门用于处理任何Owin Middleware中发生的异常(记录它并在返回之前操纵响应)客户端).
您需要将此中间件注册为类中的第一个,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, 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 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();
}
}
有几种方法可以做你想要的:
创建首先注册的中间件,然后所有异常将冒泡到该中间件.此时,只需通过OWIN上下文通过Response对象写出您的JSON.
您还可以创建包装Oauth中间件的包装中间件.在这种情况下,它将捕获源自此特定代码路径的捕获错误.
最终编写JSON消息是关于创建它,序列化它,以及通过OWIN上下文将它写入Response.
看起来你在#1的正确道路上.希望这有帮助,祝你好运:)