Tratamento de erro com o gRPC
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Este artigo descreve o tratamento de erro e o gRPC:
- Recursos de tratamento de erro interno por meio de códigos de status e mensagens de erro do gRPC.
- Envio de informações de erro complexas e estruturadas usando o tratamento avançado de erro.
Tratamento de erro interno
As chamadas gRPC comunicam o sucesso ou a falha com um código de status. Quando uma chamada gRPC é concluída com êxito, o servidor retorna um status OK
para o cliente. Se ocorrer um erro, o gRPC retornará:
- Um código de status de erro, como
CANCELLED
ouUNAVAILABLE
. - Uma mensagem de erro de cadeia de caracteres opcional.
Os tipos comumente usados com o tratamento de erro são:
StatusCode
: uma enumeração de códigos de status gRPC.OK
sinaliza o sucesso; outros valores indicam uma falha.Status
: umstruct
que combina umStatusCode
e uma mensagem de erro de cadeia de caracteres opcional. A mensagem de erro fornece mais detalhes sobre o que aconteceu.RpcException
: um tipo de exceção que tem o valorStatus
. Essa exceção é gerada nos métodos de servidor gRPC e capturada por clientes gRPC.
O tratamento de erro interno só dá suporte a um código de status e uma descrição de cadeia de caracteres. Para enviar informações de erro complexas do servidor para o cliente, use o tratamento avançado de erro.
Gerar erros do servidor
Uma chamada de servidor gRPC sempre retorna um status. O servidor retorna OK
automaticamente quando um método é concluído com êxito.
public class GreeterService : GreeterBase
{
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply { Message = $"Hello {request.Name}" });
}
public override async Task SayHelloStreaming(HelloRequest request,
IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
{
for (var i = 0; i < 5; i++)
{
await responseStream.WriteAsync(new HelloReply { Message = $"Hello {request.Name} {i}" });
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
}
O código anterior:
- Implementa o método unário
SayHello
que é concluído com êxito quando retorna uma mensagem de resposta. - Implementa o método
SayHelloStreaming
de streaming do servidor que é concluído com êxito quando o método é concluído.
Status de erro do servidor
Os métodos gRPC retornam um código de status de erro gerando uma exceção. Quando uma RpcException
é gerada no servidor, o código de status e a descrição são retornados ao cliente:
public class GreeterService : GreeterBase
{
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
if (string.IsNullOrEmpty(request.Name))
{
throw new RpcException(new Status(StatusCode.InvalidArgument, "Name is required."));
}
return Task.FromResult(new HelloReply { Message = $"Hello {request.Name}" });
}
}
Os tipos de exceção gerados que não são RpcException
também causam falha na chamada, mas com um código de status UNKNOWN
e uma mensagem genérica Exception was thrown by handler
.
Exception was thrown by handler
é enviada ao cliente em vez da mensagem de exceção, a fim de impedir a exposição de informações potencialmente confidenciais. Para ver uma mensagem de erro mais descritiva em um ambiente de desenvolvimento, configure EnableDetailedErrors
.
Tratar erros do cliente
Quando um cliente gRPC faz uma chamada, o código de status é validado automaticamente quando a resposta é acessada. Por exemplo, aguardar uma chamada gRPC unária retorna a mensagem enviada pelo servidor se a chamada é bem-sucedida e gera uma, em caso de uma falha RpcException
. Captura RpcException
para tratar erros em um cliente:
var client = new Greet.GreeterClient(channel);
try
{
var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });
Console.WriteLine("Greeting: " + response.Message);
}
catch (RpcException ex)
{
Console.WriteLine("Status code: " + ex.Status.StatusCode);
Console.WriteLine("Message: " + ex.Status.Detail);
}
O código anterior:
- Faz uma chamada gRPC unária ao método
SayHello
. - Grava a mensagem de resposta no console caso ela seja bem-sucedida.
- Captura
RpcException
e grava os detalhes do erro em caso de falha.
Cenários de erro
Os erros são representados por RpcException
com um código de status de erro e uma mensagem de detalhes opcional. RpcException
é gerado em muitos cenários:
- A chamada falhou no servidor e o servidor enviou um código de status de erro. Por exemplo, o cliente gRPC iniciou uma chamada que não tinha dados obrigatórios da mensagem de solicitação e o servidor retorna um código de status
INVALID_ARGUMENT
. - Ocorreu um erro no cliente durante uma chamada gRPC. Por exemplo, um cliente faz uma chamada gRPC, não consegue se conectar ao servidor e gera um erro com o status
UNAVAILABLE
. - O CancellationToken transmitido para a chamada gRPC é cancelado. A chamada gRPC é interrompida e o cliente gera um erro com o status
CANCELLED
. - Uma chamada gRPC excede o prazo configurado. A chamada gRPC é interrompida e o cliente gera um erro com o status
DEADLINE_EXCEEDED
.
Tratamento avançado de erro
O tratamento avançado de erro permite que informações complexas e estruturadas sejam enviadas com mensagens de erro. Por exemplo, a validação de campos de mensagem de entrada que retorna uma lista de nomes de campos inválidos e descrições. O modelo de erro google.rpc.Status
costuma ser usado para enviar informações de erro complexas entre aplicativos gRPC.
O gRPC no .NET dá suporte a um modelo de erro avançado por meio do pacote Grpc.StatusProto
. Esse pacote contém métodos para criar modelos de erros avançados no servidor e lê-los por um cliente. O modelo de erro avançado se baseia nos recursos de tratamento internos do gRPC, que podem ser usados lado a lado.
Importante
Os erros são incluídos nos cabeçalhos e, em geral, o total de cabeçalhos nas respostas é limitado a 8 KB (8.192 bytes). Verifique se os cabeçalhos que contêm erros não excedem 8 KB.
Como criar erros avançados no servidor
Erros avançados são criados por meio do Google.Rpc.Status
. Esse tipo é diferente de Grpc.Core.Status
.
O Google.Rpc.Status
tem campos de status, mensagem e detalhes. O campo mais importante é o chamado Detalhes, que é um campo repetido de valores Any
. Os detalhes são o local em que um conteúdo complexo é adicionado.
Embora qualquer tipo de mensagem possa ser usado como um conteúdo, recomendamos usar um dos conteúdos de erro padrão:
BadRequest
PreconditionFailure
ErrorInfo
ResourceInfo
QuotaFailure
O Grpc.StatusProto
inclui o ToRpcException
, um método auxiliar usado para converter Google.Rpc.Status
em um erro. Gere o erro no método do servidor gRPC:
public class GreeterService : Greeter.GreeterBase
{
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
ArgumentNotNullOrEmpty(request.Name);
return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
}
public static void ArgumentNotNullOrEmpty(string value, [CallerArgumentExpression(nameof(value))] string? paramName = null)
{
if (string.IsNullOrEmpty(value))
{
var status = new Google.Rpc.Status
{
Code = (int)Code.InvalidArgument,
Message = "Bad request",
Details =
{
Any.Pack(new BadRequest
{
FieldViolations =
{
new BadRequest.Types.FieldViolation { Field = paramName, Description = "Value is null or empty" }
}
})
}
};
throw status.ToRpcException();
}
}
}
Como ler erros avançados por um cliente
Os erros avançados são lidos por meio da RpcException
capturada no cliente. Capture a exceção e use métodos auxiliares fornecidos por Grpc.StatusCode
para obter a respectiva instância Google.Rpc.Status
:
var client = new Greet.GreeterClient(channel);
try
{
var reply = await client.SayHelloAsync(new HelloRequest { Name = name });
Console.WriteLine("Greeting: " + reply.Message);
}
catch (RpcException ex)
{
Console.WriteLine($"Server error: {ex.Status.Detail}");
var badRequest = ex.GetRpcStatus()?.GetDetail<BadRequest>();
if (badRequest != null)
{
foreach (var fieldViolation in badRequest.FieldViolations)
{
Console.WriteLine($"Field: {fieldViolation.Field}");
Console.WriteLine($"Description: {fieldViolation.Description}");
}
}
}
O código anterior:
- Faz uma chamada gRPC dentro de um try/catch que captura
RpcException
. - Chama
GetRpcStatus()
para tentar obter o modelo de erro avançado da exceção. - Chama
GetDetail<BadRequest>()
para tentar obter um conteúdoBadRequest
do erro avançado.