-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(types): Add gRPC Richer Error Model support (Examples) (#1300)
* types: remove unnecessary cloning from `ErrorDetails` getters Breaking change. * examples: add `richer-error` examples Following implementation at flemosr/tonic-richer-error.
- Loading branch information
Showing
7 changed files
with
325 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use tonic_types::StatusExt; | ||
|
||
use hello_world::greeter_client::GreeterClient; | ||
use hello_world::HelloRequest; | ||
|
||
pub mod hello_world { | ||
tonic::include_proto!("helloworld"); | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
let mut client = GreeterClient::connect("http://[::1]:50051").await?; | ||
|
||
let request = tonic::Request::new(HelloRequest { | ||
// Valid request | ||
// name: "Tonic".into(), | ||
// Name cannot be empty | ||
name: "".into(), | ||
// Name is too long | ||
// name: "some excessively long name".into(), | ||
}); | ||
|
||
let response = match client.say_hello(request).await { | ||
Ok(response) => response, | ||
Err(status) => { | ||
println!(" Error status received. Extracting error details...\n"); | ||
|
||
let err_details = status.get_error_details(); | ||
|
||
if let Some(bad_request) = err_details.bad_request() { | ||
// Handle bad_request details | ||
println!(" {:?}", bad_request); | ||
} | ||
if let Some(help) = err_details.help() { | ||
// Handle help details | ||
println!(" {:?}", help); | ||
} | ||
if let Some(localized_message) = err_details.localized_message() { | ||
// Handle localized_message details | ||
println!(" {:?}", localized_message); | ||
} | ||
|
||
println!(); | ||
|
||
return Ok(()); | ||
} | ||
}; | ||
|
||
println!(" Successfull response received.\n\n {:?}\n", response); | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
use tonic_types::{ErrorDetail, StatusExt}; | ||
|
||
use hello_world::greeter_client::GreeterClient; | ||
use hello_world::HelloRequest; | ||
|
||
pub mod hello_world { | ||
tonic::include_proto!("helloworld"); | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
let mut client = GreeterClient::connect("http://[::1]:50051").await?; | ||
|
||
let request = tonic::Request::new(HelloRequest { | ||
// Valid request | ||
// name: "Tonic".into(), | ||
// Name cannot be empty | ||
name: "".into(), | ||
// Name is too long | ||
// name: "some excessively long name".into(), | ||
}); | ||
|
||
let response = match client.say_hello(request).await { | ||
Ok(response) => response, | ||
Err(status) => { | ||
println!(" Error status received. Extracting error details...\n"); | ||
|
||
let err_details = status.get_error_details_vec(); | ||
|
||
for (i, err_detail) in err_details.iter().enumerate() { | ||
println!("err_detail[{i}]"); | ||
match err_detail { | ||
ErrorDetail::BadRequest(bad_request) => { | ||
// Handle bad_request details | ||
println!(" {:?}", bad_request); | ||
} | ||
ErrorDetail::Help(help) => { | ||
// Handle help details | ||
println!(" {:?}", help); | ||
} | ||
ErrorDetail::LocalizedMessage(localized_message) => { | ||
// Handle localized_message details | ||
println!(" {:?}", localized_message); | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
println!(); | ||
|
||
return Ok(()); | ||
} | ||
}; | ||
|
||
println!(" Successfull response received.\n\n {:?}\n", response); | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
use tonic::{transport::Server, Code, Request, Response, Status}; | ||
use tonic_types::{ErrorDetails, StatusExt}; | ||
|
||
use hello_world::greeter_server::{Greeter, GreeterServer}; | ||
use hello_world::{HelloReply, HelloRequest}; | ||
|
||
pub mod hello_world { | ||
tonic::include_proto!("helloworld"); | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct MyGreeter {} | ||
|
||
#[tonic::async_trait] | ||
impl Greeter for MyGreeter { | ||
async fn say_hello( | ||
&self, | ||
request: Request<HelloRequest>, | ||
) -> Result<Response<HelloReply>, Status> { | ||
println!("Got a request from {:?}", request.remote_addr()); | ||
|
||
// Extract request data | ||
let name = request.into_inner().name; | ||
|
||
// Create empty ErrorDetails struct | ||
let mut err_details = ErrorDetails::new(); | ||
|
||
// Add error details conditionally | ||
if name.is_empty() { | ||
err_details.add_bad_request_violation("name", "name cannot be empty"); | ||
} else if name.len() > 20 { | ||
err_details.add_bad_request_violation("name", "name is too long"); | ||
} | ||
|
||
if err_details.has_bad_request_violations() { | ||
// Add aditional error details if necessary | ||
err_details | ||
.add_help_link("description of link", "https://resource.example.local") | ||
.set_localized_message("en-US", "message for the user"); | ||
|
||
// Generate error status | ||
let status = Status::with_error_details( | ||
Code::InvalidArgument, | ||
"request contains invalid arguments", | ||
err_details, | ||
); | ||
|
||
return Err(status); | ||
} | ||
|
||
let reply = hello_world::HelloReply { | ||
message: format!("Hello {}!", name), | ||
}; | ||
Ok(Response::new(reply)) | ||
} | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
let addr = "[::1]:50051".parse().unwrap(); | ||
let greeter = MyGreeter::default(); | ||
|
||
println!("GreeterServer listening on {}", addr); | ||
|
||
Server::builder() | ||
.add_service(GreeterServer::new(greeter)) | ||
.serve(addr) | ||
.await?; | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
use tonic::{transport::Server, Code, Request, Response, Status}; | ||
use tonic_types::{BadRequest, Help, LocalizedMessage, StatusExt}; | ||
|
||
use hello_world::greeter_server::{Greeter, GreeterServer}; | ||
use hello_world::{HelloReply, HelloRequest}; | ||
|
||
pub mod hello_world { | ||
tonic::include_proto!("helloworld"); | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct MyGreeter {} | ||
|
||
#[tonic::async_trait] | ||
impl Greeter for MyGreeter { | ||
async fn say_hello( | ||
&self, | ||
request: Request<HelloRequest>, | ||
) -> Result<Response<HelloReply>, Status> { | ||
println!("Got a request from {:?}", request.remote_addr()); | ||
|
||
// Extract request data | ||
let name = request.into_inner().name; | ||
|
||
// Create empty BadRequest struct | ||
let mut bad_request = BadRequest::new(vec![]); | ||
|
||
// Add violations conditionally | ||
if name.is_empty() { | ||
bad_request.add_violation("name", "name cannot be empty"); | ||
} else if name.len() > 20 { | ||
bad_request.add_violation("name", "name is too long"); | ||
} | ||
|
||
if !bad_request.is_empty() { | ||
// Add aditional error details if necessary | ||
let help = Help::with_link("description of link", "https://resource.example.local"); | ||
|
||
let localized_message = LocalizedMessage::new("en-US", "message for the user"); | ||
|
||
// Generate error status | ||
let status = Status::with_error_details_vec( | ||
Code::InvalidArgument, | ||
"request contains invalid arguments", | ||
vec![bad_request.into(), help.into(), localized_message.into()], | ||
); | ||
|
||
return Err(status); | ||
} | ||
|
||
let reply = hello_world::HelloReply { | ||
message: format!("Hello {}!", name), | ||
}; | ||
Ok(Response::new(reply)) | ||
} | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
let addr = "[::1]:50051".parse().unwrap(); | ||
let greeter = MyGreeter::default(); | ||
|
||
println!("GreeterServer listening on {}", addr); | ||
|
||
Server::builder() | ||
.add_service(GreeterServer::new(greeter)) | ||
.serve(addr) | ||
.await?; | ||
|
||
Ok(()) | ||
} |
Oops, something went wrong.