来自 WebAPI 过滤器的 ActionExecutedContext 中的异常 属性 为空

Exception property in ActionExecutedContext from WebAPI filter is null

几个月来我一直在使用同一个异常过滤器,到目前为止它的表现一直很完美。这是:

public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
{
    public int Order { get; set; } = int.MaxValue - 10;

    public void OnActionExecuting(ActionExecutingContext context)
    {
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.Exception is HttpResponseException exception)
        {
            context.Result = new ObjectResult(new {Mensagem = context.Exception.Message})
            {
                StatusCode = exception.Status,
            };
            context.ExceptionHandled = true;
        }
        else if (context.Exception is Exception)
        {
            var message = context.Exception.InnerException != null ? context.Exception.InnerException.Message : context.Exception.Message;

            context.Result = new ObjectResult(new RetornoExceptionApi(message))
            {
                StatusCode = StatusCodes.Status500InternalServerError
            };

            context.ExceptionHandled = true;
        }
    }
}

简单明了。我一直在使用它来捕获自定义 HTTP 异常,HttpResponseException 是基础 class。这是它的样子:

public class HttpResponseException : Exception
{
    public int Status { get; set; } = StatusCodes.Status500InternalServerError;

    public HttpResponseException(int statusCode)
    {
        Status = statusCode;
    }
}

没什么。这是我的 404 状态代码的自定义异常:

public class NotFoundException404<TKey> : HttpResponseException
{
    private string _message;

    public NotFoundException404(TKey id) : base(StatusCodes.Status404NotFound)
    {
        _message = $"Nenhum objeto encontrado! ID solicitado: { id }";
    }
    public NotFoundException404(long id) : base(StatusCodes.Status404NotFound)
    {
        _message = $"Nenhum objeto encontrado! ID solicitado: { id }";
    }
    public NotFoundException404(string message) : base(StatusCodes.Status404NotFound)
    {
        _message = message;
    }

    public override string Message => _message;
}

示例用法(这是来自我的基本服务 class):

    protected virtual async Task<TEntity> GetWithFilterAsync(TKey id)
    {
        var entidade = await _repository.FirstOrDefaultAsync(GetFiltroListar(id));

        if (entidade == null)
            throw new NotFoundException404<TKey>(id);

        return entidade;
    }

就像我说的,这工作得很好,过滤器甚至捕获从框架内引发的异常。但我必须创建一个自定义异常来处理外键违规,每当我抛出此异常时,都会调用过滤器的 OnActionExecuted,但 context.Exception 为空。这是自定义异常:

public class ForeignKeyViolationException : Exception
{
    public override string Message => "Registro possui dependências e não pode ser excluído";

    public ForeignKeyViolationException() : base() { }

    public ForeignKeyViolationException(string message) : base(message) { }
}

此异常是从与 HTTP 其他服务相同的基础服务引发的,例如:

    protected async Task DeleteAsync(TKey id)
    {
        var entidade = await GetEntidadeExclusaoAsync(id);
        try
        {
            await _repository.DeleteAsync(entidade);
        }
        catch (SqlException e) when (e.Number == 547)
        {
            throw new ForeignKeyViolationException();
        }
        catch (Exception ex)
        {
            if (ex.InnerException != null
                && ex is SqlException
                && ((SqlException)ex).Number == 547)
                throw new ForeignKeyViolationException();
        }
    }

那行不通(Exception 在我的过滤器中是 null),我不知道为什么。有什么指点吗?

这里的主要问题似乎是我很愚蠢。我停下来以更彻底的方式检查代码,问题不在过滤器中,而是我如何尝试提高 ForeignKeyViolationException。这个:

    catch (Exception ex)
    {
        if (ex.InnerException != null
            && ex is SqlException
            && ((SqlException)ex).Number == 547)
            throw new ForeignKeyViolationException();
    }

应该是这样的:

    catch (Exception ex)
    {
        if (ex.InnerException != null
            && ex.InnerException is SqlException
            && ((SqlException)ex.InnerException).Number == 547)
            throw new ForeignKeyViolationException();
        else
            throw ex;
    }

"Derp!"

更新

johnny 5 的回答更简洁并且运行完美,这无疑是首选的方式:

catch (Exception ex) when ((ex.InnerException as SqlException)?.Number == 547)
{
    throw new ForeignKeyViolationException();
}

看来您已经找到问题的答案了。但是,我会努力以您的回答为基础。您可以使用 when 子句使函数更具可读性。

catch (Exception ex) when ((ex.InnerException as SqlException)?.Number == 547) {
    throw new ForeignKeyViolationException();
}