Grpc.StatusProto
2.67.0
Prefix Reserved
dotnet add package Grpc.StatusProto --version 2.67.0
NuGet\Install-Package Grpc.StatusProto -Version 2.67.0
<PackageReference Include="Grpc.StatusProto" Version="2.67.0" />
paket add Grpc.StatusProto --version 2.67.0
#r "nuget: Grpc.StatusProto, 2.67.0"
// Install Grpc.StatusProto as a Cake Addin #addin nuget:?package=Grpc.StatusProto&version=2.67.0 // Install Grpc.StatusProto as a Cake Tool #tool nuget:?package=Grpc.StatusProto&version=2.67.0
gRPC C# API for error handling with status.proto
This is a protoype NuGet package providing C# and .NET client and server side support for the gRPC richer error model.
This feature is already available in many other implementations including C++, Go, Java and Python.
This package has dependencies on these NuGet packages:
Google.Api.CommonProtos
- to provide the proto implementations used by the richer error modelGrpc.Core.Api
- for API classes such asRpcException
Error handling in gRPC
The standard way for gRPC to report the success or failure of a gRPC call is for a
status code to be returned. If a call completes successfully the server returns an OK
status to the client, otherwise an error status code is returned with an optional string
error message that provides further details about what happened. This is known as the
standard error model and is the official gRPC error model supported by all gRPC
implementations.
There is another error model known as the richer error model that allows additional error details to be included by the server. These are expressed in protocol buffers messages, and a set of standard error message types is defined to cover most needs. The protobuf binary encoding of this extra error information is provided as trailing metadata in the response.
For more information on the richer error model see the gRPC documentation on error handling, and the Google APIs overview of the error model.
.NET implementation of the richer error model
The error model is defined by the protocol buffers files status.proto
and error_details.proto,
and the Google.Api.CommonProtos
NuGet package that provides the generated .NET classes
from these proto files.
The error is encapsulated by an instance of Google.Rpc.Status
and
returned in the trailing response metadata with well-known key grpc-status-details-bin
.
Setting and reading this metadata is handled
for you when using the methods provided in this package.
Server Side
The server side uses C#'s Object and Collection initializer syntax.
The server returns the additional error information by throwing an RpcException
that is
created from a Google.Rpc.Status
which contains the details of the error.
To add messages to the Details
repeated field in Google.Rpc.Status
, wrap each one in Any.Pack()
- see example below.
The Google.Rpc.Status
extension method ToRpcException
creates the appropriate RpcException
from the status.
Example - creating and throwing a RpcException
:
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
ArgumentNotNullOrEmpty(request.Name);
return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
}
private static void ArgumentNotNullOrEmpty(string value, [CallerArgumentExpression(nameof(value))] string? paramName = null)
{
if (string.IsNullOrEmpty(value))
{
throw 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"
}
}
})
}
}.ToRpcException();
}
}
A note on error codes
Both Grpc.Core.StatusCode
and Google.Rpc.Code
define enums for a common
set of status codes such as NotFound
, PermissionDenied
, etc. They have the same values and are based on the codes defined
in grpc/status.h.
The recommendation is to use the values in Google.Rpc.Code
as a convention.
This is a must for Google APIs and strongly recommended for third party services.
But users can use a different domain of values if they want to and as long as their
services are mutually compatible, things will work fine.
In the richer error model the RpcException
will contain both a Grpc.Core.Status
(for the
standard error model) and a Google.Rpc.Status
(for the richer error model), each with their
own status code. While an application is free to set these to different values we recommend
that they are set to the same value to avoid ambiguity.
Passing stack traces from the server to the client
The richer error model defines a standard way of passing stack traces from the server to the
client. The DebugInfo
message can be populated with stack traces and then it can
be included in the Details
of the Google.Rpc.Status
.
This package includes the extension method ToRpcDebugInfo
for System.Exception
to help
create the DebugInfo
message with the details from the exception.
Example:
try
{
// ...
}
catch (Exception e)
{
throw new Google.Rpc.Status
{
Code = (int)Google.Rpc.Code.Internal,
Message = "Internal error",
Details =
{
// populate debugInfo from the exception
Any.Pack(e.ToRpcDebugInfo()),
// Add any other messages to the details ...
}
}.ToRpcException();
}
Client Side
There is an extension method to retrieve a Google.Rpc.Status
from the metadata in
an RpcException
.
Once the Google.Rpc.Status
has been retrieved the messages in the Details
can be unpacked. There are two ways of doing this:
- calling
GetDetail<T>()
with one of the expected message types - iterating over all the messages in the
Details
usingUnpackDetailMessage()
Example - calling GetDetail<T>()
:
void PrintError(RpcException ex)
{
// Get the status from the RpcException
Google.Rpc.Status? rpcStatus = ex.GetRpcStatus(); // Extension method
if (rpcStatus != null)
{
Console.WriteLine($"Google.Rpc Status: Code: {rpcStatus.Code}, Message: {rpcStatus.Message}");
// Try and get the ErrorInfo from the details
ErrorInfo? errorInfo = rpcStatus.GetDetail<ErrorInfo>();
if (errorInfo != null)
{
Console.WriteLine($"\tErrorInfo: Reason: {errorInfo.Reason}, Domain: {errorInfo.Domain}");
foreach (var md in errorInfo.Metadata)
{
Console.WriteLine($"\tKey: {md.Key}, Value: {md.Value}");
}
}
// etc, for any other messages expected in the Details ...
}
}
Example - iterating over all the messages in the Details
:
void PrintStatusDetails(RpcException ex)
{
// Get the status from the RpcException
Google.Rpc.Status? rpcStatus = ex.GetRpcStatus(); // Extension method
if (rpcStatus != null)
{
// Decode each message item in the details in turn
foreach (var msg in rpcStatus.UnpackDetailMessages())
{
switch (msg)
{
case ErrorInfo errorInfo:
Console.WriteLine($"ErrorInfo: Reason: {errorInfo.Reason}, Domain: {errorInfo.Domain}");
foreach (var md in errorInfo.Metadata)
{
Console.WriteLine($"\tKey: {md.Key}, Value: {md.Value}");
}
break;
case BadRequest badRequest:
Console.WriteLine("BadRequest:");
foreach (BadRequest.Types.FieldViolation fv in badRequest.FieldViolations)
{
Console.WriteLine($"\tField: {fv.Field}, Description: {fv.Description}");
}
break;
// Other cases handled here ...
}
}
}
Returning errors within gRPC streams
The model described above allows you to return an error status when the gRPC call finishes.
As an extension to the richer error model you may want to allow servers to send back multiple statuses when streaming responses without terminating the call.
One way of doing this is to include a google.rpc.Status
message in the definition
of the response messages returned by the server. The client should also be aware
that it may receive a status in the response.
For example:
service WidgetLookupProvider {
rpc streamingLookup(stream WidgetReq) returns (stream WidgetRsp) {}
}
message WidgetReq {
string widget_name = 1;
}
message WidgetRsp {
oneof message{
// details when ok
string widget_details = 1;
// or error details
google.rpc.Status status = 2;
}
}
Note: the status.proto
and error_details.proto
files are provided in the Google.Api.CommonProtos
NuGet package.
Example server code fragment:
await foreach (var request in requestStream.ReadAllAsync())
{
var response = new WidgetRsp();
// ... process the request ...
// to return an error
if (error)
{
response.Status = new Google.Rpc.Status { /* ... */ };
}
else
{
response.WidgetDetails = "the details";
}
}
Example client code fragment:
// reading the responses
var responseReaderTask = Task.Run(async () =>
{
await foreach (var rsp in call.ResponseStream.ReadAllAsync())
{
switch (rsp.MessageCase)
{
case WidgetRsp.MessageOneofCase.WidgetDetails:
// ... processes the details ...
break;
case WidgetRsp.MessageOneofCase.Status:
// ... handle the error ...
break;
}
}
});
// sending the requests
foreach (var request in requests)
{
await call.RequestStream.WriteAsync(request);
}
See also
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
.NET Framework | net461 was computed. net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.6.2
- Google.Api.CommonProtos (>= 2.15.0)
- Grpc.Core.Api (>= 2.67.0)
-
.NETStandard 2.0
- Google.Api.CommonProtos (>= 2.15.0)
- Grpc.Core.Api (>= 2.67.0)
-
.NETStandard 2.1
- Google.Api.CommonProtos (>= 2.15.0)
- Grpc.Core.Api (>= 2.67.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Grpc.StatusProto:
Package | Downloads |
---|---|
Device.GrpcClient
Package Description |
|
Device.Grpc
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
2.67.0 | 1,214 | 11/21/2024 |
2.67.0-pre1 | 60 | 10/22/2024 |
2.66.0 | 20,552 | 9/20/2024 |
2.66.0-pre1 | 94 | 9/6/2024 |
2.65.0 | 33,921 | 7/27/2024 |
2.65.0-pre1 | 95 | 7/20/2024 |
2.64.0 | 1,840 | 7/19/2024 |
2.64.0-pre1 | 5,543 | 7/15/2024 |
2.63.0 | 39,473 | 5/24/2024 |
2.63.0-pre1 | 104 | 5/8/2024 |
2.62.0 | 34,180 | 3/29/2024 |
2.62.0-pre1 | 187 | 3/8/2024 |
2.61.0 | 31,069 | 2/22/2024 |
2.61.0-pre1 | 593 | 2/8/2024 |