29

Let's consider a simple service:

service Something {
    rpc Do(Request) returns Response;
}

message Request {
    string field = 1;
}

message Response {
    string response = 1; 
}

Assume I have to do some checking on the Request.field, I want to raise a client error if the field is invalid:

class MyService(proto_pb2.SomethingServicer):

    def Do(self, request, context):
        if not is_valid_field(request.field):
            raise ValueError("Damn!")  # Or something like that
        return proto_pb2.Response(response="Yeah!")

With the following client:

channel = grpc.insecure_channel(...)
stub = proto_pb2.SomethingStub(channel)
try:
    response = stub.Do(proto_pb2.Request(field="invalid"))
except grpc.RpcError as e:
    print(e)

<_Rendezvous of RPC that terminated with (StatusCode.UNKNOWN, Exception calling application: Damn!)>

So I can technically handle errors. My issue is... is there a better way? Is there a good way to change the message description? Can we change the status code?

FunkySayu
  • 7,641
  • 10
  • 38
  • 61

3 Answers3

38

Yes, there is a better way. You may change the status details using the ServicerContext.set_details method and you may change the status code using the ServicerContext.set_code method. I suspect that your servicer will look something like

class MyService(proto_pb2.SomethingServicer):

    def Do(self, request, context):
        if not is_valid_field(request.field):
            context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
            context.set_details('Consarnit!')
            return proto_pb2.Response()
        return proto_pb2.Response(response='Yeah!')

.

  • 2
    Cool, thanks for the answer! Would you mind adding a small client example for the record? Sorry for the late acceptance :) – FunkySayu Dec 09 '16 at 16:12
  • I'm reluctant to add client example code for this case because what the server is communicating in this circumstance is "you, the client, have a bug". Rather than enhancing the client code to handle the server's responding this way, the bug in the client code should simply be fixed. Right? – Nathaniel Manista At Google Dec 13 '16 at 16:39
  • 3
    Maybe the client has a bug, or maybe the client got some user input that it doesn't know how to fully validate. In any case, the answer would be even more valuable if it could show how the `code` and `details` are checked on the client side. – Eric Smith Jun 06 '17 at 16:06
12

There's a new method for this too, context.abort() - it'll actually raise an exception to terminate the RPC call:

grpc.ServicerContext.abort()

Michael
  • 776
  • 6
  • 13
8

So at gRPC side someone can abort context using: grpc.ServicerContext.abort()

At client side (python):

try:
    result = {'msg', 'success'}
except grpc.RpcError as e:
    if e.code() == grpc.StatusCode.INVALID_ARGUMENT:
        result = {'msg', 'invalid arg error'}
    elif e.code() == grpc.StatusCode.ALREADY_EXISTS:
        result = {'msg', 'already exists error'}
Neeraj Bansal
  • 2,580
  • 16
  • 8