Open Source Web Application Framework for ASP.NET Core
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

14 KiB

Tratamento de Exceções

O ABP fornece uma infraestrutura integrada e oferece um modelo padrão para lidar com exceções.

  • Lida automaticamente com todas as exceções e envia uma mensagem de erro formatada padrão para o cliente em uma solicitação de API/AJAX.
  • Oculta automaticamente os erros internos de infraestrutura e retorna uma mensagem de erro padrão.
  • Fornece uma maneira fácil e configurável de localizar mensagens de exceção.
  • Mapeia automaticamente exceções padrão para códigos de status HTTP e fornece uma opção configurável para mapear exceções personalizadas.

Tratamento Automático de Exceções

O AbpExceptionFilter lida com uma exceção se qualquer uma das seguintes condições forem atendidas:

  • A exceção é lançada por uma ação do controlador que retorna um resultado de objeto (não um resultado de visualização).
  • A solicitação é uma solicitação AJAX (o valor do cabeçalho HTTP X-Requested-With é XMLHttpRequest).
  • O cliente aceita explicitamente o tipo de conteúdo application/json (por meio do cabeçalho HTTP accept).

Se a exceção for tratada, ela é automaticamente registrada e uma mensagem JSON formatada é retornada ao cliente.

Formato da Mensagem de Erro

A mensagem de erro é uma instância da classe RemoteServiceErrorResponse. O JSON de erro mais simples tem uma propriedade message conforme mostrado abaixo:

{
  "error": {
    "message": "Este tópico está bloqueado e não é possível adicionar uma nova mensagem"
  }
}

Existem campos opcionais que podem ser preenchidos com base na exceção que ocorreu.

Código de Erro

O código de erro é um valor de string opcional e único para a exceção. A exceção lançada deve implementar a interface IHasErrorCode para preencher este campo. Exemplo de valor JSON:

{
  "error": {
    "code": "App:010042",
    "message": "Este tópico está bloqueado e não é possível adicionar uma nova mensagem"
  }
}

O código de erro também pode ser usado para localizar a exceção e personalizar o código de status HTTP (consulte as seções relacionadas abaixo).

Detalhes do Erro

Os detalhes do erro são um campo opcional da mensagem de erro JSON. A exceção lançada deve implementar a interface IHasErrorDetails para preencher este campo. Exemplo de valor JSON:

{
  "error": {
    "code": "App:010042",
    "message": "Este tópico está bloqueado e não é possível adicionar uma nova mensagem",
    "details": "Informações mais detalhadas sobre o erro..."
  }
}
Erros de Validação

validationErrors é um campo padrão que é preenchido se a exceção lançada implementar a interface IHasValidationErrors.

{
  "error": {
    "code": "App:010046",
    "message": "Sua solicitação não é válida, corrija e tente novamente!",
    "validationErrors": [{
      "message": "O nome de usuário deve ter no mínimo 3 caracteres.",
      "members": ["userName"]
    },
    {
      "message": "A senha é obrigatória",
      "members": ["password"]
    }]
  }
}

AbpValidationException implementa a interface IHasValidationErrors e é automaticamente lançada pelo framework quando a entrada de uma solicitação não é válida. Portanto, geralmente você não precisa lidar com erros de validação, a menos que tenha lógica de validação altamente personalizada.

Registro

As exceções capturadas são automaticamente registradas.

Nível de Registro

As exceções são registradas com o nível Error por padrão. O nível de log pode ser determinado pela exceção se ela implementar a interface IHasLogLevel. Exemplo:

public class MinhaExcecao : Exception, IHasLogLevel
{
    public LogLevel LogLevel { get; set; } = LogLevel.Warning;

    //...
}

Exceções de Registro Próprio

Alguns tipos de exceção podem precisar escrever logs adicionais. Eles podem implementar a interface IExceptionWithSelfLogging se necessário. Exemplo:

public class MinhaExcecao : Exception, IExceptionWithSelfLogging
{
    public void Log(ILogger logger)
    {
        //...log informações adicionais
    }
}

Os métodos de extensão ILogger.LogException são usados para escrever logs de exceção. Você pode usar o mesmo método de extensão quando necessário.

Exceções de Negócios

A maioria de suas próprias exceções será exceções de negócios. A interface IBusinessException é usada para marcar uma exceção como uma exceção de negócios.

BusinessException implementa a interface IBusinessException além das interfaces IHasErrorCode, IHasErrorDetails e IHasLogLevel. O nível de log padrão é Warning.

Normalmente, você tem um código de erro relacionado a uma exceção de negócios específica. Por exemplo:

throw new BusinessException(QaErrorCodes.CanNotVoteYourOwnAnswer);

QaErrorCodes.CanNotVoteYourOwnAnswer é apenas uma const string. O seguinte formato de código de erro é recomendado:

<namespace-do-código>:<código-de-erro>

namespace-do-código é um valor único específico para o seu módulo/aplicação. Exemplo:

Volo.Qa:010002

Volo.Qa é o namespace do código aqui. O namespace do código será então usado ao localizar mensagens de exceção.

  • Você pode lançar diretamente uma BusinessException ou derivar seus próprios tipos de exceção dela quando necessário.
  • Todas as propriedades são opcionais para a classe BusinessException. Mas geralmente você define a propriedade ErrorCode ou Message.

Localização de Exceções

Um problema ao lançar exceções é como localizar mensagens de erro ao enviá-las para o cliente. O ABP oferece dois modelos e suas variantes.

Exceção Amigável ao Usuário

Se uma exceção implementa a interface IUserFriendlyException, então o ABP não altera suas propriedades Message e Details e a envia diretamente para o cliente.

A classe UserFriendlyException é a implementação integrada da interface IUserFriendlyException. Exemplo de uso:

throw new UserFriendlyException(
    "O nome de usuário deve ser único!"
);

Dessa forma, não há necessidade de localização. Se você deseja localizar a mensagem, pode injetar e usar o localizador de strings padrão (consulte o documento de localização). Exemplo:

throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage"]);

Em seguida, defina no recurso de localização para cada idioma. Exemplo:

{
  "culture": "pt",
  "texts": {
    "UserNameShouldBeUniqueMessage": "O nome de usuário deve ser único!"
  }
}

O localizador de strings já suporta mensagens parametrizadas. Por exemplo:

throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage", "john"]);

Em seguida, o texto de localização pode ser:

"UserNameShouldBeUniqueMessage": "O nome de usuário deve ser único! '{0}' já está em uso!"
  • A interface IUserFriendlyException é derivada da IBusinessException e a classe UserFriendlyException é derivada da classe BusinessException.

Usando Códigos de Erro

UserFriendlyException é bom, mas tem alguns problemas em usos avançados:

  • Requer que você injete o localizador de strings em todos os lugares e sempre o use ao lançar exceções.
  • No entanto, em alguns casos, pode não ser possível injetar o localizador de strings (em um contexto estático ou em um método de entidade).

Em vez de localizar a mensagem ao lançar a exceção, você pode separar o processo usando códigos de erro.

Primeiro, defina o mapeamento do namespace-do-código para o recurso de localização na configuração do módulo:

services.Configure<AbpExceptionLocalizationOptions>(options =>
{
    options.MapCodeNamespace("Volo.Qa", typeof(QaResource));
});

Então, qualquer uma das exceções com o namespace Volo.Qa será localizada usando seu recurso de localização fornecido. O recurso de localização deve sempre ter uma entrada com a chave do código de erro. Exemplo:

{
  "culture": "pt",
  "texts": {
    "Volo.Qa:010002": "Você não pode votar em sua própria resposta!"
  }
}

Então uma exceção de negócios pode ser lançada com o código de erro:

throw new BusinessException(QaDomainErrorCodes.CanNotVoteYourOwnAnswer);
  • Lançar qualquer exceção que implemente a interface IHasErrorCode se comporta da mesma maneira. Portanto, a abordagem de localização de código de erro não é exclusiva para a classe BusinessException.
  • Não é necessário definir uma string localizada para uma mensagem de erro. Se não estiver definido, o ABP envia a mensagem de erro padrão para o cliente. Ele não usa a propriedade Message da exceção! se você deseja isso, use a UserFriendlyException (ou use um tipo de exceção que implemente a interface IUserFriendlyException).

Usando Parâmetros de Mensagem

Se você tiver uma mensagem de erro parametrizada, poderá defini-la com a propriedade Data da exceção. Por exemplo:

throw new BusinessException("App:010046")
{
    Data =
    {
        {"UserName", "john"}
    }
};

Felizmente, há uma maneira mais simples de codificar isso:

throw new BusinessException("App:010046")
    .WithData("UserName", "john");

Em seguida, o texto localizado pode conter o parâmetro UserName:

{
  "culture": "pt",
  "texts": {
    "App:010046": "O nome de usuário deve ser único. '{UserName}' já está em uso!"
  }
}
  • WithData pode ser encadeado com mais de um parâmetro (como .WithData(...).WithData(...)).

Mapeamento de Códigos de Status HTTP

O ABP tenta determinar automaticamente o código de status HTTP mais adequado para tipos comuns de exceção seguindo estas regras:

  • Para a AbpAuthorizationException:
    • Retorna 401 (não autorizado) se o usuário não estiver logado.
    • Retorna 403 (proibido) se o usuário estiver logado.
  • Retorna 400 (requisição inválida) para a AbpValidationException.
  • Retorna 404 (não encontrado) para a EntityNotFoundException.
  • Retorna 403 (proibido) para a IBusinessException (e IUserFriendlyException já que estende a IBusinessException).
  • Retorna 501 (não implementado) para a NotImplementedException.
  • Retorna 500 (erro interno do servidor) para outras exceções (que são assumidas como exceções de infraestrutura).

O IHttpExceptionStatusCodeFinder é usado para determinar automaticamente o código de status HTTP. A implementação padrão é a classe DefaultHttpExceptionStatusCodeFinder. Pode ser substituída ou estendida conforme necessário.

Mapeamentos Personalizados

A determinação automática do código de status HTTP pode ser substituída por mapeamentos personalizados. Por exemplo:

services.Configure<AbpExceptionHttpStatusCodeOptions>(options =>
{
    options.Map("Volo.Qa:010002", HttpStatusCode.Conflict);
});

Inscrevendo-se nas Exceções

É possível ser informado quando o Framework ABP manipula uma exceção. Ele registra automaticamente todas as exceções no logger padrão, mas você pode querer fazer mais.

Nesse caso, crie uma classe derivada da classe ExceptionSubscriber em sua aplicação:

public class MeuAssinanteDeExcecao : ExceptionSubscriber
{
    public async override Task HandleAsync(ExceptionNotificationContext context)
    {
        //TODO...
    }
}

O objeto context contém informações necessárias sobre a exceção ocorrida.

Você pode ter vários assinantes, cada um recebe uma cópia da exceção. As exceções lançadas pelo seu assinante são ignoradas (mas ainda registradas).

Exceções Integradas

Alguns tipos de exceção são automaticamente lançados pelo framework:

  • AbpAuthorizationException é lançada se o usuário atual não tiver permissão para realizar a operação solicitada. Consulte autorização para mais informações.
  • AbpValidationException é lançada se a entrada da solicitação atual não for válida. Consulte validação para mais informações.
  • EntityNotFoundException é lançada se a entidade solicitada não estiver disponível. Isso é lançado principalmente por repositórios.

Você também pode lançar esses tipos de exceção em seu código (embora raramente seja necessário).

AbpExceptionHandlingOptions

AbpExceptionHandlingOptions é o principal objeto de opções para configurar o sistema de tratamento de exceções. Você pode configurá-lo no método ConfigureServices do seu módulo:

Configure<AbpExceptionHandlingOptions>(options =>
{
    options.SendExceptionsDetailsToClients = true;
    options.SendStackTraceToClients = false;
});

Aqui está uma lista das opções que você pode configurar:

  • SendExceptionsDetailsToClients (padrão: false): Você pode habilitar ou desabilitar o envio de detalhes da exceção para o cliente.
  • SendStackTraceToClients (padrão: true): Você pode habilitar ou desabilitar o envio da pilha de chamadas da exceção para o cliente. Se você deseja enviar a pilha de chamadas para o cliente, deve definir tanto as opções SendStackTraceToClients quanto SendExceptionsDetailsToClients como true, caso contrário, a pilha de chamadas não será enviada para o cliente.

Veja Também