Reputation: 401
I want know about good practices with golang and gRPC and protobuf.
I am implementing the following gRPC service
service MyService {
rpc dosomethink(model.MyModel) returns (model.Model) {
option (google.api.http) = { post: "/my/path" body: "" };
}
}
I compiled the protobufs. In fact, the protobuf give us a httpproxy from http to grpc.
The code to implement this service:
import "google.golang.org/grpc/status"
func (Abcd) Dosomethink(c context.Context, sessionRequest *model.MyModel) (*model.Model, error) {
return nil, status.New(400,"Default error message for 400")
}
I want a 400 http error (in the http proxy) with the message "Default error message for 400", the message works, but the http error always is 500.
Do you know any post or doc about this?
Upvotes: 12
Views: 19911
Reputation: 26
In gRPC, we should never return error in success response. It should be like either we return the successResponse or we return the errorResponse. If we are return for example errorResponse then the successResponse will be nil
.
Golang has a package called status
which is widely used for error handling. It has an internal function Error()
which converts it to error
object which is returned.
// status.proto
message Status {
// The status code, which should be an enum value of [google.rpc.Code]
int32 code = 1;
// error-message
string message = 2;
// A list of messages that carry the error details.
repeated google.protobuf.Any details = 3;
}
So, We should send grpc error code in code
field. We can send a general message related to error occured or ErrorCodes like "VALIDATION_ERROR", "AUTH_ERROR", etc. in message
field. And, the additional information or stack trace or developer error message should be sent in details
field.
Example 1:
err := status.Errorf(codes.InvalidArgument, "First Name is required")
Example 2:
//error.proto
message ErrorDetails {
string code = 1;
string additional_info = 2;
}
err := status.Errorf(codes.InvalidArgument, "First Name is required")
err, _ := err.WithDetails(&ErrorDetails{
Code: "VALIDATION_ERROR",
AdditionalInfo: "Error occured at third-party API"
})
We can also create our own custom struct similar to Status which should implement 2 methods - Error()
and GRPCStatus()
and gRPC will take care of rest.
Note: In case, we are using grpc-gateway and serving both grpc and http servers via proxy. In that scenerio, we can create a custom interceptor using WithErrorHandler
to covert status
object to our more client-friendly error object.
Upvotes: 0
Reputation: 3273
You need to return empty model.Model
object in order for protobufs to be able to properly serialise the message.
Try
import "google.golang.org/grpc/status"
func (Abcd) Dosomethink(c context.Context, sessionRequest *model.MyModel) (*model.Model, error) {
return &model.Model{}, status.Error(400,"Default error message for 400")
}
Upvotes: 13
Reputation: 610
Error Handler:
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
return data, status.Errorf(
codes.InvalidArgument,
fmt.Sprintf("Your message", req.data),
)
For need more info about the error handling take a look below links.
https://grpc.io/docs/guides/error.html
Upvotes: 11