One challenge is that e.g. Grpc.AspNetCore.Server doesn't have dependency on Google.Protobuf, which is needed to be able to add the status.proto message, so this support probably needs to come as a separate nuget package (we should start thinking about creating the metapackage, because we now have more nugets and we'll probably add some more).
Glad to see there's already an issue open on this.
I wonder if it would be possible to provide an extended RpcException<T> with a detail object, similar to WCF's FaultException<T>, which has a Details property of T, where T is a Protobuf type.
Would probably look like this:
using pb = global::Google.Protobuf;
public class RpcException<T> where T : pb:IMessage<T>
{
public T Details { get; }
}
And maybe the client could add extra code to look for the Status.details property and convert it to the generic RpcException<T>? Guessing that would be in the Core library rather than this one?
If it was added then it should probably be added to grpc/grpc. The type constraint couldn't be IMessage<T>. There is no dependency on Protobuf in the libraries.
Could you link to an article showing how it is useful?
I'll try to look into providing a nuget package with google.rpc.status support (which would be usable by both Grpc.Core and grpc-dotnet), but can't promise any ETA.
Especially the BadRequest with field violations is must. Otherwise .NET people are bound to reinvent the wheel for basic validation e.g. "Username has wrong character" "You must be at least 21 years old".
https://grpc.io/docs/guides/error/
This richer error model is already supported in the C++, Go, Java, Python, and Ruby libraries, and at least the grpc-web and Node.js libraries have open issues requesting it. Other language libraries may add support in the future if there鈥檚 demand, so check their github repos if interested.
Example how it's used in Go lang ( https://jbrandhorst.com/post/grpc-errors/ )
st := status.New(codes.InvalidArgument, "One or more fields has errors")
v := &errdetails.BadRequest_FieldViolation{
Field: "username",
Description: "The username must only contain alphanumeric characters",
}
br := &errdetails.BadRequest{}
br.FieldViolations = append(br.FieldViolations, v)
st, err := st.WithDetails(br)
...
(Code snippet editted from above example page)
In my opinion, this is bare minimum all API should support, standard way to convey which fields has incorrect input.
@jtattermusch if you are working on this as a separate package I have a suggestion for API:
In server side, for each standard error message have a new exception e.g. with BadRequestException which could inherit from RpcException:
```c#
public override Task
{
var error = new BadRequestException()
if (usernameIsNotValid(username)) {
error.AddFieldViolation("username", "Username has wrong chars")
}
if (ageIsnotValid(age)) {
error.AddFieldViolation("age", "You are too young to view this");
}
if (error.HasFieldViolations()) {
throw error;
}
return Task.FromResult(new MyReply
{
});
}
On Client side you could just catch it:
```c#
try {
var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
}
catch (BadRequestException ex) {
ex.GetFieldViolations();
// ...
}
I found example how it's used in the Java side https://github.com/avinassh/grpc-errors/issues/10
Status s = com.google.rpc.Status.newBuilder()
.setCode(com.google.rpc.Code.INVALID_ARGUMENT.getNumber())
.setMessage("Invalid age")
.addDetails(Any.pack(BadRequest.newBuilder()
.addFieldViolations(BadRequest.FieldViolation
.newBuilder()
.setField("age")
.setDescription("age must be positive ")
.build()
).build()
)
).build();
throw io.grpc.protobuf.StatusProto.toStatusRuntimeException(s);
Needless to say, that's a bit complicated, it could be simplified if it's done by separate package.
For example it's pointless to require build Invalid Argument, when the spec says it should always be invalid argument with BadRequest. In otherwords the package could abstract that detail away.
I've published a small NuGet package with extension methods that that add rudimentary support for the richer gRPC error model:
https://github.com/nano-byte/grpc-rich-error
https://www.nuget.org/packages/GrpcRichError/
What is the best pattern to use if I want to have properties of type google.rpc.* within my own proto messages?
This is somewhat outside the scope of the original issue, but I can't find a way to properly consume / use those protos and this is the only (even slightly) relevant issue I could find.
The Google.Protobuf.Tools package has the very basic protobuf namespace and files (./tools/google/protobuf), and I was hoping something similar existed for the rpc namespace seeing as the Grpc.Core namespace has its own definition of those exact same objects ('Grpc.Core.StatusCode', 'Grpc.Core.Status', both of which are used in 'RpcException').
Is there no such thing / no way to consume the google/rpc/ (or any namespace within the google api's project) within proto for use in a C# solution?
@JimHume: Google publishes the NuGet package Google.Api.CommonProtos with a C# implementation of the google.rpc.Status and others like google.rpc.BadRequest message.
I have a fully worked example of the grpc-status-details-bin convention that uses this package in the https://github.com/chwarr/grpc-dotnet-google-rpc-status repository.
This this package doesn't work, the .proto files for these messages live at https://github.com/googleapis/googleapis/tree/master/google/rpc. You could, for example, add this repository as a submodule of your own repository and consume the .proto files in your own projects.
Ah, very interesting--I hadn't considered the submodule idea.
The proto's are what I'm truly after but knowing that there are nuget's is very helpful. I didn't realize that the CommonProtos nuget was a thing despite searching for all things 'google' and 'api'.
This is great, thanks @chwarr .
Most helpful comment
I found example how it's used in the Java side https://github.com/avinassh/grpc-errors/issues/10
Needless to say, that's a bit complicated, it could be simplified if it's done by separate package.
For example it's pointless to require build Invalid Argument, when the spec says it should always be invalid argument with BadRequest. In otherwords the package could abstract that detail away.