首页 > 解决方案 > WebAPI 过滤器的 ActionExecutedContext 中的异常属性为空

问题描述

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

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;
        }
    }
}

简单直接。我一直在使用它来捕获HttpResponseException作为基类的自定义 HTTP 异常。这是它的样子:

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;
}

示例用法(来自我的基础服务类):

    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();
        }
    }

这不起作用(Exceptionnull我的过滤器中),我不知道为什么。任何指针?

标签: c#exceptionasp.net-core-3.0

解决方案


这里的主要问题似乎是我很愚蠢。我停下来以更彻底的方式检查代码,问题不在于过滤器,而在于我如何尝试提高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;
    }

“噗!”

更新

johnny 5's answer更干净,效果很好,这肯定是首选的方法:

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

推荐阅读