diff --git a/docs/pt-BR/Exception-Handling.md b/docs/pt-BR/Exception-Handling.md new file mode 100644 index 0000000000..288badbdff --- /dev/null +++ b/docs/pt-BR/Exception-Handling.md @@ -0,0 +1,343 @@ +# 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: + +````json +{ + "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: + +````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: + +```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`. + +````json +{ + "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: + +````C# +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: + +````C# +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: + +````C# +throw new BusinessException(QaErrorCodes.CanNotVoteYourOwnAnswer); +```` + +`QaErrorCodes.CanNotVoteYourOwnAnswer` é apenas uma `const string`. O seguinte formato de código de erro é recomendado: + +```` +: +```` + +**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: + +````C# +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](Localization.md)). Exemplo: + +````C# +throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage"]); +```` + +Em seguida, defina no **recurso de localização** para cada idioma. Exemplo: + +````json +{ + "culture": "pt", + "texts": { + "UserNameShouldBeUniqueMessage": "O nome de usuário deve ser único!" + } +} +```` + +O localizador de strings já suporta **mensagens parametrizadas**. Por exemplo: + +````C# +throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage", "john"]); +```` + +Em seguida, o texto de localização pode ser: + +````json +"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: + +````C# +services.Configure(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: + +````json +{ + "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: + +````C# +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: + +````C# +throw new BusinessException("App:010046") +{ + Data = + { + {"UserName", "john"} + } +}; + +```` + +Felizmente, há uma maneira mais simples de codificar isso: + +````C# +throw new BusinessException("App:010046") + .WithData("UserName", "john"); +```` + +Em seguida, o texto localizado pode conter o parâmetro `UserName`: + +````json +{ + "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: + +````C# +services.Configure(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](Logging.md), mas você pode querer fazer mais. + +Nesse caso, crie uma classe derivada da classe `ExceptionSubscriber` em sua aplicação: + +````csharp +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](Authorization.md) para mais informações. +- `AbpValidationException` é lançada se a entrada da solicitação atual não for válida. Consulte [validação](Validation.md) para mais informações. +- `EntityNotFoundException` é lançada se a entidade solicitada não estiver disponível. Isso é lançado principalmente por [repositórios](Repositories.md). + +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](Options.md) para configurar o sistema de tratamento de exceções. Você pode configurá-lo no método `ConfigureServices` do seu [módulo](Module-Development-Basics.md): + +````csharp +Configure(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 + +* [Tutorial em vídeo](https://abp.io/video-courses/essentials/exception-handling)