mirror of https://github.com/abpframework/abp.git
1 changed files with 343 additions and 0 deletions
@ -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>:<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: |
||||
|
|
||||
|
````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<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: |
||||
|
|
||||
|
````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<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](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<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 |
||||
|
|
||||
|
* [Tutorial em vídeo](https://abp.io/video-courses/essentials/exception-handling) |
||||
Loading…
Reference in new issue