I try to find out the way to report server errors via grpc. What I want to achieve is errors that are verbose and static defined enough to tell clients what actually happened.
Imagine I have the next request:
message UpdateEmailRequest {
string email = 1;
}
message UpdateEmailResponse {
}
Then I was trying to make errors more verbose because status codes don't tell clients about error details. You could use FieldViolation, but it only specifies the field, not the error in the static way. You could provide Description to these FieldViolations but clients need to either deal with string errors and somehow map them to internal client errors OR clients display these errors directly, which doesn't work in some cases because business logic can vary based on the error type.
What I've invented is:
message UpdateEmailResponse {
enum ErrorCode {
UNKNOWN_ERROR = 0;
INVALID_EMAIL_LENGTH = 1;
INVALID_EMAIL_FORMAT = 2;
INVALID_EMAIL_TEMPORARY_MAIL_NOT_ALLOWED = 3; //temporary mail services
INVALID_EMAIL_ALIAS = 4; // email is alias or redirect, not the actual email
INVALID_EMAIL_FORBIDDEN_WORDS = 5; // for email with bad language
...
}
message Body {}
oneof response {
ErrorCode error_code = 1;
Body body = 2;
}
}
This way you could let the clients know about specific errors combined with status codes, but it doesn't use the out-of-box FieldViolations and it looks like non-grpc way to handle errors.
Other solution is to embed json with error code (which I can still have defined inside the request entity) and error msg inside the FieldViolations's Description, but again, it looks like non-grpc way.
So, I'm stuck here with errors in grpc, could you tell me the proper way to do it?
Thanks.
Related
I have a microservices based application and wish to create a service that captures all Fault events with their message payloads (as json) and stores them in a database for later analysis and potential resubmission. I have created a Fault consumer and can capture the Fault but am unable to generically extract the message payload as json.
public Task Consume(ConsumeContext<Fault> context)
{
if (context is PipeContext pipeContext)
{
var result = pipeContext.TryGetPayload(out ConsumeContext<Fault> payload2);
var serCont = context.SerializerContext;
}
Console.WriteLine($"A message faulted:{context.Message.FaultedMessageId} " +
$"{context.Message.Exceptions} " +
$"{context.ConversationId}"
);
return Task.CompletedTask;
}
I can see the full details I want in the context.SerializerContext._message but this is unaccessable.
context.SerializerContext._message
I saw you comment for a similar question:
If you did want to later get the actual fault message, you could use
consumeContext.TryGetMessage<Fault>(out var faultContext) and if it
was present you'd get it back.
I don't have "T" from every service and therefore want to handle all Faults a JSON.
Is there a way I can capture the full Fault with the message, ideally as json, without having access to every T across my system?
I am on MassTransit 8.
Thanks
If you have the message type (T), you can use TryGetMessage<Fault<T>> and it will return the message type deserialized.
If you don't, or if you want to deal with the JSON in a message directly, using V8 you can get the actual JsonElement from the deserializer and navigate the JSON yourself:
var jsonElement = context.TryGetMessage<JsonElement>()
Previous answer, but for Newtonsoft: https://stackoverflow.com/a/46779547/1882
I am implementing a simple client-server grpc-c++ based application. In the Hello rpc, I am taking the request and sending the fields of another message called SeverInfo as response. The problem is I exactly don't know how to send this ServerInfo data to a client from server side. We basically use set_fieldname(ex: set_name) for general datatypes to send the data but how should we send this serverInfo data to HelloResponse and then to HelloRequest. Can somebody please help me??
Below I am attaching the proto file.
syntax = "proto3";
package sample;
service Sample {
rpc Hello(HelloRequest) returns (HelloReply){}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
ServerInfo sinfo = 1;
}
message ServerInfo {
string name = 1;
string os = 2;
}
you can define another rpc in your service definitions like
service Sample {
rpc Hello(HelloRequest) returns (HelloReply){}
rpc GetServerInfo(HelloRequest) returns (ServerInfo){}
}
would that work for you?
Here is the answer that worked for me. Thank you.
ServerInfo* serverinfo=new ServerInfo();
serverinfo->set_name("");
serverinfo->set_os("");
HelloReply* rep;
rep->set_allocated_server(serverinfo);
We have a use case where, we have many RPC defined in different-different .proto files , and we generate a java based grpc stub code by using google's protobuf-java & protoc-gen-grpc-java as gradle plugin.
The requirement is we want to generate a new Service which flips the request, response and add stream to new flipped rpc.
So for example :
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
to be converted to like
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHelloStreaming (stream HelloReply) returns (stream HelloRequest) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
In java generated code I should be having 2 services for each original service. We just want the final java generated code to be having 2 services, the parser may/may not update original .proto files.
Is this customization possible with current protoc ? Can we extend the plugin and write ours -> Can someone please give some pointers.
Your question is unclear to me.
Revising proto files is a fundamental requirement of gRPC.
The Java tutorial on https://grpc.io includes an example of adding a method to a service. In part, this is because adding|removing|updating methods|messages|fields is a common behavior.
NOTE To clarify nomenclature, in your example, you're proposing adding a method to an existing service (definition). If you consider the proto as defining an API, this represents a non-breaking change. See Versioning gRPC services for a good overview. Existing clients will continue to work (they are only aware of SayHello) while new clients will be aware of SayHelloStreaming too.
I have a java spring integration project that is receving emails through the below code:
ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext(
"/integration/gmail-imap-idle-config.xml");
DirectChannel inputChannel = ac.getBean("receiveChannel", DirectChannel.class);
inputChannel.subscribe(message -> {
org.springframework.messaging.Message<MimeMailMessage> received =
(org.springframework.messaging.Message<MimeMailMessage>) message;
log.info("content" + message);
List<String> sentences = null;
try {
} catch (Exception e) {
}
I get the email, and I can get the subject, but I can never actually extract the message body. How do I do this?
Thank you!
You have to use this option on the channel adapter:
simple-content="true"
See its description:
When 'true', messages produced by the source will be rendered by 'MimeMessage.getContent()'
which is usually just the body for a simple text email. When false (default) the content
is rendered by the 'getContent()' method on the actual message returned by the underlying
javamail implementation.
For example, an IMAP message is rendered with some message headers.
This attribute is provided so that users can enable the previous behavior, which just
rendered the body.
But still it is doubtful, since I see in case of GMail message it is never simple. The content is a MimeMultipart and we need to read its parts to get access to the real body.
So, this is how you should change your code as well:
log.info("content" + ((MimeMultipart) ((MimeMessage) message.getPayload()).getContent()).getBodyPart(0).getContent());
So I'm getting an error code 400 with a keyInvalid reason:
json = {
error = {
code = 400;
errors = (
{
domain = usageLimits;
message = "Bad Request";
reason = keyInvalid;
}
);
message = "Bad Request";
};
}
I'm using the correct API call I'm sure:
https://www.googleapis.com/language/translate/v2?key=INSERT-YOUR-KEY&q=hello%20world&source=en&target=de
And for INSERT-YOUR-KEY I've tried both the Client ID and the Client secret.
Also, I do have the Translate API turned on in the console.
Oh lord, what a waste of time - mostly my fault! So I needed to create a public API key, which was the second option on the page staring right in the face this whole time. One of those days...