Могу ли я отправить пользовательское сообщение об ошибке с сервера на клиент GRPC?

Я создал простой GRPC-сервер и клиент.

Я хочу создать пользовательскую ошибку на сервере и передать ее клиенту. Мой код выглядит следующим образом:

Server.js

var error = require('error');

var PROTO_PATH = grpc.load(__dirname + '/proto/hello.proto');
var hello_proto = PROTO_PATH.hello;

function sayHello(call, callback) {

    try {
        var jsErr = new Error('MY_ERROR');
        jsErr.newStatus = 401;
        jsErr.newMessage = 'custom unAuthorized error';
        console.log(Object.getOwnPropertyNames(jsErr));
        console.log(jsErr);
        callback(jsErr);

    } catch(e) {
        callback(e);
    }
}

function sayHelloAgain(call, callback) {
    callback(null, {message: 'Hello Again ' + call.request.name});
}

function main() {

    var server = new grpc.Server();
    server.addProtoService(hello_proto.Hello.service, {sayHello: sayHello,sayHelloAgain: sayHelloAgain });
    server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure());
    server.start();
}

main();

Client.js

var grpc = require('grpc');

var PROTO_PATH = grpc.load(__dirname + '/proto/hello.proto');
var hello_proto = PROTO_PATH.hello;

function main() {
    var client = new hello_proto.Hello('localhost:50051',grpc.credentials.createInsecure());
    var user;
    if (process.argv.length >= 3) {
        user = process.argv[2];
    } else {
        user = 'world';
    }

    client.sayHello({name: user}, function(err, response) {

        console.log(Object.getOwnPropertyNames(err));
        console.log(err);
    });
}

main();

и мой файл прото

syntax = "proto3";

package hello;

service Hello {
    rpc sayHello(sayHelloRequest) returns (sayHelloResponse) {}
    rpc sayHelloAgain(sayHelloRequest) returns (sayHelloResponse) {}
}


message sayHelloRequest {
    string name = 1;
}

message sayHelloResponse {
    string message = 1;
}

когда я запускаю cient, результат от каждого выглядит так

Сервер

[ 'stack', 'message', 'newStatus', 'newMessage' ]
{ [Error: MY_ERROR] newStatus: 401, newMessage: 'custom unAutorized error' }

Клиент

[ 'stack', 'message', 'code', 'metadata' ]
{ [Error: MY_ERROR] code: 2, metadata: Metadata { _internal_repr: {} } }

Так что мои созданные ошибки JavaScript newStatus, newMessage свойства удалены, и оно преобразовано в стандартное сообщение об ошибке GRPC.

Мои вопросы

  1. Можно ли отправить пользовательское сообщение клиенту?
  2. Могу ли я создать ошибку GRPC, а не ошибку JavaScript?
  3. один из способов отправки пользовательских атрибутов клиенту, я думаю, это добавить пользовательские данные в Metadata, но я также не уверен, как это сделать.

3 ответа

На этот же вопрос есть полезный ответ в группе Google gRPC: https://groups.google.com/d/msg/grpc-io/X_bUx3T8S7s/x38FU429CgAJ

Вы можете отправить пользовательское сообщение о состоянии клиенту, используя свойство сообщения объекта Error. В вашем примере это "MY_ERROR". Код состояния должен быть в свойстве code, так же, как вы видите его на стороне клиента.

Если вы хотите использовать структуру состояния gRPC вместо ошибки JavaScript, вы можете сделать это, заполнив свойство "code" и свойство "message" или "details" объекта.

Если вы хотите отправить метаданные, вы должны создать экземпляр класса grpc.Metadata, а затем добавить пары ключ / значение в результирующий объект. Затем вы можете передать его в качестве третьего аргумента обратного вызова или установить свойство метаданных ошибки, чтобы отправить его клиенту с ошибкой.

Обратите внимание, что коды состояния, которые использует gRPC, являются не кодами состояния HTTP, а специальными кодами gRPC, которые определены в grpc.status. Вы должны только установить свойство кода ошибки, используя эти коды. Если вы хотите отправить свои собственные коды, используйте вместо этого метаданные.

Я проиллюстрирую то, что написано выше, на нескольких примерах.

Чтобы отправить пользовательское сообщение с ошибкой, создайте Error с сообщением. Это устанавливает message имущество:

var jsErr = new Error('Unauthorized');

Как упомянуто выше, вероятно, в вашем случае напрямую устанавливать коды состояния gRPC бесполезно. Но, для справки, код состояния gRPC может быть установлен через ошибку code имущество:

jsErr.code = grpc.status.PERMISSION_DENIED;

Чтобы отправить свои собственные коды ошибок или другую информацию, используйте метаданные:

var metadata = new grpc.Metadata();
metadata.set('key1', 'value2');
metadata.set('key2', 'value2');

jsErr.metadata = metadata;

Теперь, если сервер создает ошибку, как указано выше, и клиент выводит возвращенную ошибку с помощью:

console.log(Object.getOwnPropertyNames(err));
console.log(err);
console.log(err.metadata);

тогда вывод клиента:

[ 'stack', 'message', 'code', 'metadata' ]
{ [Error: Unauthorized]
  code: 7,
  metadata: Metadata { _internal_repr: { key1: [Object], key2: [Object] } } }
Metadata { _internal_repr: { key1: [ 'value2' ], key2: [ 'value2' ] } }

1. Да 2. Может

Избегайте отправки специальных объектов (например, new Error) по проводу. Отправьте простой объект со свойством error и найдите его значение на другом конце. См. http://json.org/ чтобы получить обзор легко переносимых данных.

внутри Server.js попробуйте

function sayHello(call, callback) {

    try {
        var myCustomError = {};
        myCustomError.newStatus = 401;
        myCustomError.newMessage = 'custom unAuthorized error';
        console.log(Object.getOwnPropertyNames(myCustomError ));
        console.log(myCustomError);
        callback(null, {error: myCustomError, message: ""});

    } catch(e) {
        callback(e);
    }
}

внутри Client.js

client.sayHello({name: user}, function(err, response) {
    var myCustomError= response.error;
    if (myCustomError) {
        console.log(Object.getOwnPropertyNames(myCustomError));
        console.log(myCustomError);
    }
});

Согласно определению ServerErrorResponse, который вы можете вернуть как первый параметр в вашем callback, вы можете создать для этого простую функцию (TypeScript).

      function grpcError(): ServerErrorResponse {
    return {
        message: 'Something wrong', // required param
        name: 'Error',              // required param
        code: Status.UNKNOWN,       // code: 2
        metadata: undefined,
        details: undefined,
        stack: undefined,
     };
};

Status это перечисление от 0 до 16

      export declare enum Status {
    OK = 0,
    CANCELLED = 1,
    UNKNOWN = 2,
    INVALID_ARGUMENT = 3,
    DEADLINE_EXCEEDED = 4,
    NOT_FOUND = 5,
    ALREADY_EXISTS = 6,
    PERMISSION_DENIED = 7,
    RESOURCE_EXHAUSTED = 8,
    FAILED_PRECONDITION = 9,
    ABORTED = 10,
    OUT_OF_RANGE = 11,
    UNIMPLEMENTED = 12,
    INTERNAL = 13,
    UNAVAILABLE = 14,
    DATA_LOSS = 15,
    UNAUTHENTICATED = 16
}

Итак, пример кода для сервера

      let error = grpcError();
error.message = "YOUR CUSTOM ERROR MESSAGE";
callback(error, null);

И для клиента

      client.sayHello({name: user}, function(err, response) {
    console.log(err);
});

Результат будет похож на

      Error: 2 UNKNOWN: YOUR CUSTOM ERROR MESSAGE
// stacktrace
{
  code: 2,
  details: 'YOUR CUSTOM ERROR MESSAGE',
  metadata: Metadata { internalRepr: Map(0) {}, options: {} }
}
Другие вопросы по тегам