【问题标题】:ASP.NET Core MVC (former MVC 6) Razor errors not getting caught by exception filterASP.NET Core MVC(前 MVC 6)Razor 错误未被异常过滤器捕获
【发布时间】:2016-04-28 16:44:52
【问题描述】:

背景:使用 Dapper 和存储库模式的 ASP.NET 5 (ASP.NET Core 1.0) MVC 6 应用程序

显然,与其他所有网站/应用程序一样,我正在尝试消除我网站中弹出的大多数/所有异常。

我实现了一个ExceptionFilter 来捕获所有未处理的异常,如下所示:

public class UnhandledExceptionFilter : ActionFilterAttribute, IExceptionFilter
{
    private readonly IErrorRepo _errorRepo;

    public UnhandledExceptionFilter(IErrorRepo errorRepo)
    {
        _errorRepo = errorRepo;
    }

    public void OnException(ExceptionContext context)
    {
        try
        {
            _errorRepo.SaveException(context.Exception);
        }
        catch { }
    }
}

当错误来自C# 代码时,这很有效。但是我故意在我的 razor 视图(cshtml 文件)中添加了错误,而这些错误并没有被这个过滤器捕获。

是否需要继承其他属性/接口才能捕获 razor 异常?

更新:

这里是指定过滤器属性的地方,在configureServices方法的startup.cs文件中。

    services.AddMvc(options =>
    {
        options.Filters.Add(new UnhandledExceptionFilter(new ErrorRepo(Configuration)));
    });

【问题讨论】:

  • 我假设您在操作方法上使用该属性?如果是这样,您必须了解 mvc 管道是如何工作的。视图只有在方法返回后才会呈现(请注意,您从方法返回 View())。因此,当呈现视图时,该属性不再在范围内。你需要一个全局的 ErrorFilter 来处理这个问题,你可以使用 Will 的例子
  • @ErikFunkenbusch 是的,抱歉,请参阅我对问题的更新...
  • 嗯,您的示例与 Will 的不同之处在于 Will 使用 ServiceFilterAttribute 来允许 DI 系统实例化过滤器。它应该在没有 ServiceFilterAttribute 的情况下工作,但您可能需要删除 ActionFilter 接口。
  • 啊,所以 actionfilter 属性将其限制为仅控制器操作....
  • 我不知道,但可能是它先找到了 ActionFilter 而没有看到 ExceptionFilter

标签: c# razor asp.net-core asp.net-core-mvc


【解决方案1】:

这样做的诀窍不在于属性——它是通过添加一个中间件提供者。一旦您的中间件进入管道,您将能够捕获任何时候抛出的异常(因此不再需要您的属性)。

记录器

这实际上是要记录错误的东西。我已经复制了我从您的IErrorRepo 界面看到的内容,但是您当然可以对其进行修改以包含传递到下面Log 方法中的任何附加信息。

public class UnhandledExceptionLogger : ILogger
{
    private readonly IErrorRepo _repo;

    public UnhandledExceptionLogger(IErrorRepo repo)
    {
        _repo = repo;
    }
    public IDisposable BeginScopeImpl(object state) =>
        new NoOpDisposable();

    public bool IsEnabled(LogLevel logLevel) =>
        logLevel == LogLevel.Critical || logLevel == LogLevel.Error;

    public void Log(LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
    {
        if (IsEnabled(logLevel))
        {
            _repo.SaveException(exception);
        }
    }

    private sealed class NoOpDisposable : IDisposable
    {
        public void Dispose()
        {
        }
    }
}

提供者

这是创建记录器的工厂。它只会被实例化一次,并在通过管道时创建所有记录器。

public class UnhandledExceptionLoggerProvider : ILoggerProvider
{
    private readonly IErrorRepo _repo;

    public UnhandledExceptionLoggerProvider(IErrorRepo repo)
    {
        _repo = repo;
    }

    public ILogger CreateLogger(string categoryName) =>
        new UnhandledExceptionLogger(_repo);

    public void Dispose()
    {
    }
}

注册提供者

一旦添加到ILoggerFactory,它将在管道中的每个请求上调用。这通常是通过自定义扩展方法完成的,但我们已经有了很多新代码,所以我将省略这部分。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddProvider(new UnhandledExceptionLoggerProvider(new ErrorRepo()));
    // the rest

【讨论】:

  • 酷,我今晚会处理这个,谢谢!看起来我只需要从我的 UnhandledExceptionFilter 更改为具有 UnhandledExceptionFilter 类型的 ServiceFilterAttribute
  • @ganders 是的,我还添加了一些关于属性本身的细节。我意识到我的只是源自ExceptionFilterAttribute 类。
  • 嗯,我的设置完全一样,但它仍然没有捕捉到剃刀错误
  • 还有其他建议吗?
  • @ganders 是的,实际上!原来该属性不是我的错误记录器工作的原因。我们甚至不需要属性。一会儿我会更新。
【解决方案2】:

我使用以下代码处理所有异常(包括 Razor 异常):

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseBrowserLink();
}
else
{
    var exceptionHandlingOptions = new ExceptionHandlerOptions()
    {
        ExceptionHandler = ExceptionHandler.OnException // !! this is the key line !!
        // ExceptionHandlingPath = "/Home/Error"
        // according to my tests, the line above is useless when ExceptionHandler is set
        // after OnException completes the user would receive empty page if you don't write to Resonse in handling method
        // alternatively, you may leave ExceptionHandlingPath without assigning ExceptionHandler and call ExceptionHandler.OnException in controller's action instead
        // write me if you need a sample code
    };
    app.UseExceptionHandler(exceptionHandlingOptions);
}

public static class ExceptionHandler
{
    public static async Task OnException(HttpContext context)
    {
        var feature = context.Features.Get<IExceptionHandlerFeature>();

        var exception = feature?.Error;

        if (exception == null) return;

        //TODO: log exception here
    }
}

不要忘记删除 IExceptionFilter,因为它也会处理一些异常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-11
    • 1970-01-01
    • 2016-05-09
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    • 2016-10-10
    • 2012-08-24
    相关资源
    最近更新 更多