注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。
本篇文章討論錯誤處理和 gRPC:
- 使用 gRPC 狀態代碼和錯誤訊息的內建錯誤處理功能。
- 使用 豐富的錯誤處理傳送複雜結構化的錯誤資訊。
內建錯誤處理
gRPC 呼叫會以狀態程式碼傳達成功或失敗。 gRPC 呼叫成功完成時,伺服器會將 OK
狀態傳回給用戶端。 如果發生錯誤,gRPC 會傳回:
- 錯誤狀態程式碼,例如
CANCELLED
或UNAVAILABLE
。 - 選擇性字串錯誤訊息。
錯誤處理常用的類型如下:
StatusCode
: gRPC 狀態程式碼的列舉。OK
表示成功;其他值是失敗。Status
:struct
結合StatusCode
和選擇性字串錯誤訊息。 錯誤訊息提供有關所發生狀況的進一步詳細資訊。RpcException
:具有Status
值的例外狀況類型。 此例外狀況會在 gRPC 伺服器方法中引發,並由 gRPC 客戶端攔截。
內建錯誤處理僅支援狀態程式碼和字串描述。 若要將複雜的錯誤資訊從伺服器傳送到用戶端, 請使用豐富的錯誤處理。
引發伺服器錯誤
gRPC 伺服器呼叫一律會傳回狀態。 當方法成功完成時,伺服器會自動傳回 OK
。
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));
}
}
}
上述 程式碼:
- 實作一元
SayHello
方法,這個方法會在傳回回應訊息時順利完成。 - 在方法完成時,實作伺服器串流
SayHelloStreaming
方法成功完成。
伺服器錯誤狀態
gRPC 方法透過引發例外狀況來傳回錯誤狀態程式碼。 當伺服器引發 RpcException
時,其狀態程式碼和描述會傳回給用戶端:
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}" });
}
}
引發的例外狀況類型,不是 RpcException
也會導致呼叫失敗,但具有 UNKNOWN
狀態程式碼和通用訊息 Exception was thrown by handler
。
Exception was thrown by handler
會傳送至用戶端,而不是例外狀況訊息,以防止暴露潛在的敏感性資訊。 若要在開發環境中查看更具描述性的錯誤訊息,請設定 EnableDetailedErrors
。
處理客戶端錯誤
當 gRPC 用戶端進行呼叫時,狀態程式碼會在存取回應時自動驗證。 例如,等候一元 gRPC 呼叫會傳回伺服器在呼叫成功時所傳送的訊息,倘若失敗則引發擲回 RpcException
。 攔截 RpcException
以處理用戶端的錯誤:
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);
}
上述 程式碼:
- 對
SayHello
方法進行一元 gRPC 呼叫。 - 如果成功,請將回應訊息寫入主控台。
- 擷取
RpcException
並寫出失敗的錯誤詳細資料。
錯誤狀況
錯誤會以錯誤狀態程式碼和選擇性詳細資料訊息來表示 RpcException
。 RpcException
在許多狀況中會被引發:
- 伺服器的呼叫失敗,伺服器傳了錯誤狀態程式碼。 例如,gRPC 用戶端啟動呼叫,遺漏要求訊息的必要資料,而伺服器傳回
INVALID_ARGUMENT
狀態程式碼。 - 執行 gRPC 呼叫時,用戶端內發生錯誤。 例如,用戶端執行 gRPC 呼叫、無法連線到伺服器,並引發
UNAVAILABLE
狀態的錯誤。 - 傳遞至 gRPC 呼叫的 CancellationToken 已取消。 gRPC 呼叫已停止,且用戶端擲回狀態為
CANCELLED
的錯誤。 - gRPC 呼叫超過其設定的期限。 gRPC 呼叫已停止,且用戶端擲回狀態為
DEADLINE_EXCEEDED
的錯誤。
豐富的錯誤處理
豐富的錯誤處理可讓複雜結構化資訊與錯誤訊息一起傳送。 例如,驗證的內送郵件欄位會傳回無效欄位名稱和描述清單。 google.rpc.Status
錯誤模型 通常用在 gRPC 應用程式之間傳送複雜的錯誤資訊。
.NET 上的 gRPC 支援使用 Grpc.StatusProto
套件的豐富錯誤模型。 此套件具有在伺服器上建立豐富錯誤模型的方法,並由用戶端讀取它們。 豐富的錯誤模型建置在 gRPC 的內建處理功能之上,而且可以並存使用。
重要
錯誤會包含在標頭中,回應中的標頭總數通常限制為 8 KB(8,192 位元組)。 請確定包含錯誤的標頭不超過 8 KB。
在伺服器上建立豐富的錯誤
從 Google.Rpc.Status
建立豐富的錯誤。 此類型 不同 於 Grpc.Core.Status
。
Google.Rpc.Status
具有狀態、訊息和詳細資料欄位。 最重要的欄位是詳細資料,它是 Any
值的重複欄位。 詳細資料是新增複雜承載的位置。
雖然任何訊息類型都可以作為承載使用,但建議使用其中一個標準錯誤承載:
BadRequest
PreconditionFailure
ErrorInfo
ResourceInfo
QuotaFailure
Grpc.StatusProto
包含 ToRpcException
要轉換成 Google.Rpc.Status
錯誤的協助程式方法。 從 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();
}
}
}
用戶端讀取豐富的錯誤
從用戶端中攔截到的 RpcException
讀取豐富錯誤。 攔截例外狀況,並使用 Grpc.StatusCode
所提供的協助程式方法來取得其 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}");
}
}
}
上述 程式碼:
- 在攔截
RpcException
的 try/catch 內進行 gRPC 呼叫。 - 呼叫
GetRpcStatus()
嘗試從例外狀況取得豐富的錯誤模型。 - 呼叫
GetDetail<BadRequest>()
嘗試從豐富錯誤取得BadRequest
承載。