The GO community has standardized on using the status
package for sending rich errors in gRPC services back to the client.
This way we can send a status with a BadRequest
error, and that error message itself could contain many FieldViolation
errors with information about which field had an error and what the error was.
Those errors are fairly easy to retrieve and use from another GO gRPC client, but in my case I wanted a solution to get that information from a PHP client and there was very little information available.
This shows how to do it:
<?php declare(strict_types=1);
use GPBMetadata\Google\Rpc\ErrorDetails;
use Google\Rpc\Status;
use Google\Rpc\BadRequest;
[$response, $status] = $client->SomeRemoteCall($request)->wait();
if ($status->code !== \Grpc\STATUS_OK) {
ErrorDetails::initOnce(); // Ensures that the protobuf descriptor is registered
$details = $status->metadata["grpc-status-details-bin"][0] ?? null; // GO errors / status will be under this key
if (is_null($details)) {
throw new HttpException($message, $code, (array) $status);
}
$s = new Status();
$s->mergeFromString($details);
$errors = [];
foreach ($s->getDetails() as $anyDetails) {
$deets = $anyDetails->unpack(); // The details are a repeated Any field.
$deets->discardUnknownFields(); // Because this was a "Any" field, it has unknown fields such as @type
if ($deets instanceof BadRequest) {
foreach ($deets->getFieldViolations() as $fv) {
$errors[$fv->getField()] = [
'message' => $fv->getDescription(),
];
}
} else {
$jsonDeets = $deets->serializeToJsonString(); // encode / decode the protobuf to json to get an array
$errors[$deets->getField()] = json_decode($jsonDeets);
}
}
// Formatted error with main error and a list of cause errors:
// 'errors' [
// 'field-name' => 'message describing the error'
// ]
$error = [
'message' => $s->getMessage(),
'errors' => $errors,
];
}
The Status
, ErrorDetails
and BadRequest
classes come from the package: google/common-protos
so be sure to composer require google/common-protos
Top comments (1)
Thank you so much!