我正在测试一个MVC 6 Web Api,并希望实现登录到全局错误处理程序。只是保证在没有记录的情况下没有错误退出系统。我创建了一个ExceptionFilterAttribute并在启动时全局添加它:
public class AppExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
//Notice pulling from HttpContext Application Svcs -- don't like that
var loggerFactory = (ILoggerFactory)context.HttpContext.ApplicationServices.GetService(typeof (ILoggerFactory));
var logger = loggerFactory.Create("MyWeb.Web.Api");
logger.WriteError(2, "Error Occurred", context.Exception);
context.Result = new JsonResult(
new
{
context.Exception.Message,
context.Exception.StackTrace
});
}
}
现在,在启动时,我将此过滤器添加到:
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new AppExceptionFilterAttribute());
});
这一切似乎都是一种蛮力...有没有更好的方法来使用MVC 6?
我不喜欢或不确定这种方法:
我能想到的另一个选择是让一个基本控制器接受所有控制器继承的ILoggerFactory。
想知道是否存在允许插入日志的某种诊断中间件......
答案 0 :(得分:11)
你的问题有2个部分。 1)DI可注射过滤器2)全局错误处理。
关于#1:您可以将ServiceFilterAttribute
用于此目的。
例如:
//Modify your filter to be like this to get the logger factory DI injectable.
public class AppExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly ILogger _logger;
public AppExceptionFilterAttribute(ILoggerFactory loggerfactory)
{
_logger = loggerFactory.CreateLogger<AppExceptionFilterAttribute>();
}
public override void OnException(ExceptionContext context)
{
//...
}
}
//Register your filter as a service (Note this filter need not be an attribute as such)
services.AddTransient<AppExceptionFilterAttribute>();
//On the controller/action where you want to apply this filter,
//decorate them like
[ServiceFilter(typeof(AppExceptionFilterAttribute))]
public class HomeController : Controller
{
....
}
您应该能够从传递的ExceptionContext
获取控制器的详细信息。
关于#2:从您之前的帖子看起来您正在玩ExceptionHandlerMiddleware
(source&amp; extension source)...如何使用它?...有关的信息它:
答案 1 :(得分:1)
执行全局错误处理的另一种方法是使用ILoggerProvider
。
以这种方式记录异常的优点是它还捕获在属性无法捕获的位置发生的错误。例如,也可以记录Razor代码中出现的异常。
以下是依赖注入的基本示例:
public sealed class UnhandledExceptionLoggerProvider : ILoggerProvider
{
private readonly IMyErrorRepository errorRepo;
public UnhandledExceptionLoggerProvider(IMyErrorRepository errorRepo)
{
// inject whatever you need
this.errorRepo = errorRepo;
}
public ILogger CreateLogger(string categoryName) =>
new UnhandledExceptionLogger(errorRepo);
public void Dispose()
{
}
}
public class UnhandledExceptionLogger : ILogger
{
private readonly IMyErrorRepository errorRepo;
public UnhandledExceptionLogger(IMyErrorRepository errorRepo)
{
this.errorRepo = errorRepo;
}
public IDisposable BeginScope<TState>(TState state) =>
new NoOpDisposable();
public bool IsEnabled(LogLevel logLevel) =>
logLevel == LogLevel.Critical || logLevel == LogLevel.Error;
public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception exception,
Func<TState, Exception, string> formatter)
{
if (IsEnabled(logLevel))
{
errorRepo.LogError(exception);
}
}
private sealed class NoOpDisposable : IDisposable
{
public void Dispose()
{
}
}
}
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddTransient<IMyErrorRepository, MyErrorRepository>();
services.AddTransient<UnhandledExceptionLoggerProvider>();
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
UnhandledExceptionLoggerProvider provider)
{
loggerFactory.AddProvider(provider);
// ... all the rest of your startup code
}
答案 2 :(得分:0)
我正在使用ASP.NET Core,但这个解决方案应该可行。
我创建了一个中间件来记录通过管道的所有请求。在他们中,我只是将它包装在try catch中,所以如果它抛出异常,它会记录到我的数据库中。
public async Task Invoke(HttpContext context)
{
var sessionId = GetSessionId(context);
var path = context.Request.Path;
var startTime = DateTime.UtcNow;
var watch = Stopwatch.StartNew();
try
{
await _next.Invoke(context);
watch.Stop();
}
catch (Exception exception)
{
watch.Stop();
await _errorRepo.SaveException(exception, context.Connection.RemoteIpAddress.ToString(), sessionId);
}
finally
{
#pragma warning disable 4014
_requestLogRepo.LogRequest(
sessionId,
context.User.Identity.Name,
context.Connection.RemoteIpAddress.ToString(),
context.Request.Method,
path,
context.Request.ContentType,
context.Request.ContentLength,
startTime,
watch.ElapsedMilliseconds);
#pragma warning restore 4014
}
}