Copying the starwars example for actix-web I'm having issues understanding these errors when trying to instrument:
#[derive(Debug)] // adding this
struct AppState {
gql_schema: Schema<QueryRoot, EmptyMutation, EmptySubscription>, // errors here (description below)
// other fields here...
}
async fn main() -> std::io::Result<()> {
let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription)
.data(StarWars::new())
.finish();
println!("Playground: http://localhost:8000");
HttpServer::new(move || {
App::new()
.app_data(Data::new(AppState {
gql_schema: gql_schema.clone(), // using it here
}))
.service(web::resource("/").guard(guard::Post()).to(index))
.service(web::resource("/").guard(guard::Get()).to(index_playground))
})
.bind("127.0.0.1:8000")?
.run()
.await
}
#[tracing::instrument()] // here another error
async fn index(app_state: web::Data<AppState>, req: GraphQLRequest) -> GraphQLResponse {
app_state.gql_schema.execute(req.into_inner()).await.into()
}
the errors are:
`async_graphql::Schema<starwars::QueryRoot, async_graphql::EmptyMutation, async_graphql::EmptySubscription>` doesn't implement `std::fmt::Debug`
the trait `std::fmt::Debug` is not implemented for `async_graphql::Schema<starwars::QueryRoot, async_graphql::EmptyMutation, async_graphql::EmptySubscription>` rustc E0277
server.rs(13, 10): Error originated from macro call here
`async_graphql_actix_web::GraphQLRequest` doesn't implement `std::fmt::Debug`
the trait `std::fmt::Debug` is not implemented for `async_graphql_actix_web::GraphQLRequest`
the trait `tracing::Value` is implemented for `tracing::field::DebugValue<T>`
required because of the requirements on the impl of `std::fmt::Debug` for `&async_graphql_actix_web::GraphQLRequest`
required because of the requirements on the impl of `tracing::Value` for `tracing::field::DebugValue<&async_graphql_actix_web::GraphQLRequest>`
required for the cast to the object type `dyn tracing::Value` rustc E0277
I tried to add tracing feature too in Cargo.toml, but it's the same:
async-graphql = { version = "4.0.6", features = ["tracing"] }
How can I fix?
I think that, rather than attempting to #[instrument] the handler yourself, you should use the Tracing extension. This will put a span around each entry point to the schema, which you can then see whenever a tracing event is emitted from a resolver.
You already have the "tracing" feature, so you'll want to add these lines:
use async_graphql::extensions::Tracing;
let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription)
.data(StarWars::new())
.extension(Tracing) // Add this line
.finish();
I put in the line tracing::info!("Human::name"); in the Human::name resolver in the StarWars data, and got out this log line when querying it (using tracing-subcriber::fmt):
INFO request:execute:field{path=hero parent_type=QueryRoot return_type=Character!}:field{path=hero.name parent_type=Character return_type=String!}: starwars::model: Human::name
Related
I have been building an http server in Rust to handle requests from a React client. I am using the rocket framework to manage the server configuration and the sqlx crate to create and perform actions on a connection pool. I need asynchronous handler functions to allow awaiting the database connection within the handler, but I can't seem to get the compiler to agree with the handler's return type. I've attempted to write a custom implementation of the Responder trait for sending my User struct as a json string, but the compiler continues to suggest that my struct has inadequate implementation. I've tried responding with serde_json, which is pre-equipped with the Responder implementation, but then the compiler complains that the future generated by the async handler lacks the implementation... I am incredibly new to this inspiring but perplexing language, so I can only hope this question isn't entirely foolish... Any suggestions you have based on prior experience with orchestrating communication between a Rust mysql connection and Rocket server would be outstandingly appreciated!
Here is the main server config:
pub async fn main() {
//
// Rocket Server Handler
//
let options = Options::Index | Options::DotFiles;
let sql_pool = db::main().await;
rocket::ignite()
.attach(CORS())
.mount("/api/login", routes![login])
.mount(
"/",
StaticFiles::new(concat!(env!("CARGO_MANIFEST_DIR"), "/static"), options),
)
.manage(DBConn {
connection: sql_pool,
})
.launch();
}
And here is the login route that won't compile:
#[post("/", format = "application/json", data = "<input>")]
pub async fn login(sql_pool: State<DBConn, '_>, input: Json<LoginInput>) -> Json<String> {
let input = input.into_inner();
println!("{:?}", input);
let is_valid = db::models::User::login_by_email(
&sql_pool.connection,
"jdiehl2236#gmail.com".to_string(),
"supersecret".to_string(),
)
.await;
let response = LoginResponse {
username: "userguy".to_string(),
email: "email#gmail.com".to_string(),
success: is_valid,
};
Json(serde_json::to_string(&response).unwrap())
}
And the compiler error:
the trait bound `impl Future<Output = rocket_contrib::json::Json<std::string::String>>: Responder<'_>` is not satisfied
the following other types implement trait `Responder<'r>`:
<&'r [u8] as Responder<'r>>
<&'r str as Responder<'r>>
<() as Responder<'r>>
<Flash<R> as Responder<'r>>
<JavaScript<R> as Responder<'r>>
<JsonValue as Responder<'a>>
<LoginResponse as Responder<'r>>
<MsgPack<R> as Responder<'r>>
and 29 othersrustcClick for full compiler diagnostic
handler.rs(202, 20): required by a bound in `handler::<impl Outcome<rocket::Response<'r>, rocket::http::Status, rocket::Data>>::from`
I'm getting blocked on what I think it's a simple problem. I'm still learning Rust, and I want to do the following:
I want to create an async trait (using async-trait) that will instantiate a DB connection instance and it will return the struct that is implementing that trait.
mongo.rs
#[async_trait]
pub trait DB {
async fn init<T, E>() -> Result<T, E>;
}
Then: favorites.rs (See the implementation of the DB trait down below)
use async_trait::async_trait;
use mongodb::Collection;
use rocket::form::FromForm;
use rocket::serde::ser::StdError;
use serde::{Deserialize, Serialize};
use std::error::Error;
use uuid::Uuid;
pub struct FavoritesDB {
collection: Collection<Favorite>,
}
#[derive(Debug)]
pub enum FavoritesError {
UnknownError(Box<dyn Error>),
}
// Conflicts with the one down below
// impl From<Box<dyn Error>> for FavoritesError {
// fn from(err: Box<dyn Error>) -> FavoritesError {
// FavoritesError::UnknownError(err)
// }
// }
impl From<Box<dyn StdError>> for FavoritesError {
fn from(err: Box<dyn StdError>) -> FavoritesError {
FavoritesError::UnknownError(err)
}
}
#[async_trait]
impl mongo::DB for FavoritesDB {
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError> {
let main_db = mongo::init::<Favorite>("Favorites").await?;
let db = FavoritesDB {
collection: main_db.collection,
};
Ok(db)
}
}
There are a list of problems with this:
1)
error[E0574]: expected struct, variant or union type, found type parameter `FavoritesDB`
--> src\db\favorites.rs:41:18
|
41 | let db = FavoritesDB {
| ^^^^^^^^^^^ not a struct, variant or union type
|
help: consider importing this struct instead
I've tried implementing From<Box<dyn tdError>> manually but it conflicts with what I have.
error[E0277]: `?` couldn't convert the error to `FavoritesError`
--> src\db\favorites.rs:40:65
|
40 | let main_db = mongo::init::<Favorite>("Favorites").await?;
| ^ the trait `From<Box<dyn StdError>>` is not implemented for `FavoritesError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, Box<dyn StdError>>>` for `Result<FavoritesDB, FavoritesError>`
note: required by `from_residual`
--> C:\Users\asili\.rustup\toolchains\nightly-2021-11-15-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\ops\try_trait.rs:339:5
|
339 | fn from_residual(residual: R) -> Self;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider further restricting this bound
|
39 | async fn init<FavoritesDB, FavoritesError + std::convert::From<std::boxed::Box<dyn std::error::Error>>>() -> Result<FavoritesDB, FavoritesError> {
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Some errors have detailed explanations: E0277, E0282, E0574.
For more information about an error, try `rustc --explain E0277`.
Just for more context, here's the DB struct and impl (Currently connecting to a local MongoDB) included in mongo.rs
pub struct Database<T> {
client: mongodb::Database,
pub collection: Collection<T>,
}
impl<T> Database<T> {
pub async fn init() -> Result<mongodb::Database, Box<dyn Error>> {
let mut client_options = ClientOptions::parse("mongodb://localhost:27017").await?;
client_options.app_name = Some("My App".to_string());
// Get a handle to the deployment.
let client = Client::with_options(client_options)?;
let db = client.database("rust-svelte");
return Ok(db);
}
}
pub async fn init<T>(collection: &str) -> Result<Database<T>, Box<dyn Error>> {
let client = Database::<T>::init().await?;
let collection = client.collection::<T>(collection);
let db = Database { client, collection };
Ok(db)
}
I've been searching for a few days over SO and the Rust community and my Google-Rust-Fu isn't good enough to spot what's the problem. Any ideas?
You've declared init to take 2 generic parameters: T and E.
This means that the code that calls init has to provide the concrete types to fill in those parameters. For example, if someone was using your library, it would be totally feasible for them to write init::<i64, ()>(), and your code should deal with that.
Because of that, when you define your impl DB for FavouritesDB, you write this:
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError>
This is no different to writing:
async fn init<T, E>() -> Result<T, E>
you've just given the type parameters different names that happen to match a struct that you're probably trying to use.
A better pattern might be an associated type. Instead of the caller deciding what the concrete types are (as is the case with generics), with associated types, the implementation of the trait on the type sets the type.
This is common with things like Iterator. Iterator has no generic parameters, but a single associated type Item. This is because it wouldn't make sense to be able to impl Iterator<String> for MyStruct and impl Iterator<i64> for MyStruct at the same time. Instead, we want to implement Iterator for a type once, and that implementation carries with it the definition of the types it expects.
So something like this (I've omitted the async-ness for brevity since it doesn't seem to be a factor here):
trait DB {
type InitOk;
type InitErr;
fn init() -> Result<Self::InitOk, Self::InitErr>;
}
impl Db for FavouritesDB {
type InitOk = FavouritesDB;
type InitErr = FavouritesError;
fn init() -> Result<Self::InitOk, Self::InitErr> {
// now you can reference FavouritesDB the struct, rather than the generic parameter
}
}
I'd also add you may want to not have the InitOk type, and just return Self, but that's up to you if you think you might want a struct to be able to create a different type.
For part 2, Rust assumes nothing (other than Sized) about generic parameters. If you want Rust to force a generic to have some property, you have to add a bound.
The compiler is telling you here that it can't use the ? operator to convert automatically, because it doesn't know that your error type has a From<Box<dyn Error>> implementation.
If you know that every error type is going to implement that, you can add it as a bound on the associated type, like this:
trait DB {
type InitOk;
type InitErr: From<Box<dyn Error>>;
// ...
}
I want to use reqwest to write a rest client, follow the official document https://github.com/seanmonstar/reqwest and add dependencies like this in toml:
reqwest = {version = "0.11.4", feature = ["blocking", "json"]}
and this is my code:
use std::collections::HashMap;
pub fn get_user_info(id: i64) -> &'static str {
let resp = reqwest::get("https://httpbin.org/ip")
.json::<HashMap<String, String>>();
println!("{:#?}", resp);
return "ok";
}
when I compile this code, shows error like this:
error[E0599]: no method named `json` found for opaque type `impl std::future::Future` in the current scope
--> src/common/net/rest_client.rs:5:10
|
5 | .json::<HashMap<String, String>>();
| ^^^^ method not found in `impl std::future::Future`
why would this happen? what should I do to fix this problem?
You are using asynchronous version of the GET API. If you want to use the blocking one, Use reqwest::blocking::get instead.
Since you enabled the blocking feature in Cargo.toml, I am assuming that you intended to use the blocking API.
use std::collections::HashMap;
pub fn get_info() -> &'static str {
let resp = reqwest::blocking::get("https://httpbin.org/ip")
.unwrap()
.json::<HashMap<String, String>>()
.unwrap();
println!("{:#?}", resp);
return "ok";
}
If you still want to use async API, you have to await the result before deserializing the response body.
When I tried to build an application with axum, I failed to separate the framework from my handler. With Go, the classic way is define an Interface, implement it and register the handler to framework. In this way, it's easy to provide a mock handler to test with. However, I couldn't make it work with Axum. I defined a trait just like above, but it wouldn't compile:
use std::net::ToSocketAddrs;
use std::sync::{Arc, Mutex};
use serde_derive::{Serialize, Deserialize};
use serde_json::json;
use axum::{Server, Router, Json};
use axum::extract::Extension;
use axum::routing::BoxRoute;
use axum::handler::get;
#[tokio::main]
async fn main() {
let app = new_router(
Foo{}
);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
trait Handler {
fn get(&self, get: GetRequest) -> Result<GetResponse, String>;
}
struct Foo {}
impl Handler for Foo {
fn get(&self, req: GetRequest) -> Result<GetResponse, String> {
Ok(GetResponse{ message: "It works.".to_owned()})
}
}
fn new_router<T:Handler>(handler: T) -> Router<BoxRoute> {
Router::new()
.route("/", get(helper))
.boxed()
}
fn helper<T:Handler>(
Extension(mut handler): Extension<T>,
Json(req): Json<GetRequest>
) -> Json<GetResponse> {
Json(handler.get(req).unwrap())
}
#[derive(Debug, Serialize, Deserialize)]
struct GetRequest {
// omited
}
#[derive(Debug, Serialize, Deserialize)]
struct GetResponse {
message: String
// omited
}
error[E0599]: the method `boxed` exists for struct `Router<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>>>`, but its trait bounds were not satisfied
--> src/router.rs:25:10
|
25 | .boxed()
| ^^^^^ method cannot be called on `Router<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>>>` due to unsatisfied trait bounds
|
::: /Users/lebrancebw/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-0.2.5/src/routing/mod.rs:876:1
|
876 | pub struct Layered<S> {
| --------------------- doesn't satisfy `<_ as tower_service::Service<Request<_>>>::Error = _`
|
= note: the following trait bounds were not satisfied:
`<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>> as tower_service::Service<Request<_>>>::Error = _`
I guess the key point is my design is apparently not "rustic". Is there a way to structure an Axum project that lends itself to testing easily?
The question is what you want to test. I will assume that you have some core logic and an HTTP layer. And you want to make sure that:
requests are routed correctly;
requests are parsed correctly;
the core logic is called with the expected parameters;
and return values from the core are correctly formatted into HTTP responses.
To test it you want to spawn an instance of the server with the core logic mocked out.
#lukemathwalker in his blog and book "Zero To Production In Rust" has a very nice description of how to spawn an app for testing through the actual TCP port. It is written for Actix-Web, but the idea applies to Axum as well.
You should not use axum::Server::bind, but rather use axum::Server::from_tcp to pass it a std::net::TcpListner which allows you to spawn a test server on any available port using `TcpListener::bind("127.0.0.1:0").
To make the core logic injectable (and mockable) I declare it as a struct and implement all the business methods on it. Like this:
pub struct Core {
public_url: Url,
secret_key: String,
storage: Storage,
client: SomeThirdPartyClient,
}
impl Core {
pub async fn create_something(
&self,
new_something: NewSomething,
) -> Result<Something, BusinessErr> {
...
}
With all these pieces you can write a function to start the server:
pub async fn run(listener: TcpListener, core: Core)
This function should encapsulate things like routing configuration, server logging configuration and so on.
Core can be provided to handlers using Extension Layer mechanism like this:
...
let shared_core = Arc::new(core);
...
let app = Router::new()
.route("/something", post(post_something))
...
.layer(AddExtensionLayer::new(shared_core));
Which in a handler can be declared in parameter list using extension extractor:
async fn post_something(
Extension(core): Extension<Arc<Core>>,
Json(new_something): Json<NewSomething>,
) -> impl IntoResponse {
core
.create_something(new_something)
.await
}
Axum examples contain one on error handling and dependency injection. You can check it here.
Last but not least, now you can mock Core out with a library like mockall, write spawn_app function that would return host and port where the server is run, run some requests against it and do assertions.
The video from Bogdan at Let's Get Rusty channel provides a good start with mockall.
I will be happy to provide more details if you feel something is missing from the answer.
I am creating a rust web application. I am new to this thing and I am trying to make API requests and pass a result form a request as Response to the web view. There are main.rs , route.rs and common.rs files. Basically main.rs file call relevant route and then route will call the function.
But, there is an error with a function in the route.rs file.
error[E0277]: the trait bound `fn() -> impl std::future::Future {<search::routes::get_token as actix_web::service::HttpServiceFactory>::register::get_token}: actix_web::handler::Factory<_, _, _>` is not satisfied
--> src\search\routes.rs:20:10
|
20 | async fn get_token() -> Result<String, reqwest::Error> {
| ^^^^^^^^^ the trait `actix_web::handler::Factory<_, _, _>` is not implemented for `fn() -> impl std::future::Future {<search::routes::get_token as actix_web::service::HttpServiceFactory>::register::get_token}`
How can I Fix this ??
route.rs
use crate::search::User;
use actix_web::{get, post, put, delete, web, HttpResponse, Responder};
use serde_json::json;
extern crate reqwest;
extern crate serde;
mod bfm;
mod common;
#[get("/token")]
async fn get_token() -> Result<String, reqwest::Error> {
let set_token = common::authenticate();
// let set_token = common::get_rust_posts();
return set_token.await;
//HttpResponse::Ok().json(set_token)
}
common.rs
extern crate reqwest;
use reqwest::header::HeaderMap;
use reqwest::header::AUTHORIZATION;
use reqwest::header::CONTENT_TYPE;
pub async fn authenticate() -> Result<String, reqwest::Error> {
fn construct_headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(AUTHORIZATION, "Basic bghythjuikyt==".parse().unwrap());
headers.insert(CONTENT_TYPE, "application/x-www-form-urlencoded".parse().unwrap());
assert!(headers.contains_key(AUTHORIZATION));
assert!(!headers.contains_key(CONTENT_TYPE));
headers
}
let client = reqwest::Client::new();
let reszr = client.post("https://api.test.com/auth/token")
.headers(construct_headers())
.body("grant_type=client_credentials")
.send()
.await?
.text()
.await;
return reszr;
}
First of all, please read: https://actix.rs/docs/errors/
--
Do you have implemented ResponseError for reqwest::Error?
You have to implement ResponseError trait because framework have to response with something in case when Result is Err.
Here is an example
impl ResponseError for Error {
fn error_response(&self) -> HttpResponse {
match self.kind_ref() {
ErrorKind::HttpResponse { msg, code, real } => {
println!("{}", real);
HttpResponse::build(code.to_owned()).json(json!({
"status": false,
"msg": msg
}))
}
_ => {
println!("{}", self);
HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).finish()
}
}
}
}
Error in this case is my custom error that contains msg, code, real, but the logic should be similar in your case.
--
UPDATE: after #Masklinn comment about error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
Code above works only on custom Error you own.
What you can to do is:
map reqwest::Error to Error that already implements ResponseError. You can check list of implementations here: https://docs.rs/actix-web/3.0.0-alpha.1/actix_web/trait.ResponseError.html#implementors
create custom Error that wraps reqwest::Error (and other errors in your code) and implement ResponseError on it (this is basicaly same/similar as first)