I initialized a Mutex<Pool<Postgres>> instance and passed it to manage method on Rocket
instance in order to access the database on my controllers. What I usually do is:
// this is purely for example
#[get("/foo/bar")]
pub async fn controller<'a>(
// ... others ...
db_pool: &State<Mutex<PgPool>>,
// ... others ...
) {
// ... do other things ...
let query_r = sqlx::query("SELECT * FROM users WHERE id=$1")
.bind(&id)
.fetch_optional(&mut db_pool.inner().lock().await.acquire().await.unwrap()) // i reach `inner` of state, then `lock` the mutex, lastly `acquire` a pool connection
.await; // type is Result<Option<PgRow>> i suppose, rust-analyzer is not very good at inferring it
// ... do other things ...
}
This is cool and all but here's my problem: I wrote a struct as below...
pub struct User {
id: usize,
username: String,
email: String,
}
...and I actually want to use it as request guard so that the client cannot hit the controllers if they haven't provided the correct credentials.
Official guide of Rocket tells to implement FromRequest trait so that it can be resolved as guard.
However, in order to initialize a User
instance, I need to get a PoolConnection inside this FromRequest trait. I have dived into Rocket's API documentation and seems like request parameter on FromRequest has a method named rocket with returns a Rocket<Orbit>
instance, and, with this, I can access the state.
However, the problem is that state method returns &Mutex<Pool<Postgres>>
, which is an immutable reference, so I cannot do any operations on database with this.
I have come this far and would like to ask if anyone knows a way to access Mutex<Pool<Postgres>>
without a reference.
Thanks in advance.
How Far I Have Come
Below is how I have implemented FromRequest.
pub struct User {
id: usize,
username: String,
email: String,
}
#[derive(Debug)]
pub enum UserError {
TokenError(TokenError),
MissingToken,
DatabasePoolError,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for User {
type Error = UserError;
async fn from_request(request: &'r rocket::Request<'_>) -> request::Outcome<Self, Self::Error> {
let encrypted_token_o = request.headers().get_one("Authorization");
match encrypted_token_o {
Some(encrypted_token) => {
let claims_r = Claims::try_from(encrypted_token.to_owned());
match claims_r {
Ok(claims) => {
let db_pool_o = request.rocket().state::<Mutex<PgPool>>();
match db_pool_o {
Some(db_pool) => {
let id = 0; // a test id
let query_r = sqlx::query(
"SELECT id, username, email FROM users WHERE id=$1",
)
.bind(&id)
.fetch_optional(
// ERROR: no method named `inner` found for reference `&rocket::futures::lock::Mutex<Pool<Postgres>>` in the current scope
&mut db_pool.inner().lock().await.acquire().await.unwrap(),
)
.await;
todo!()
}
None => request::Outcome::Failure((
http::Status::InternalServerError,
UserError::DatabasePoolError,
)),
}
}
Err(e) => request::Outcome::Failure((
http::Status::InternalServerError,
UserError::TokenError(e),
)),
}
}
None => request::Outcome::Failure((http::Status::BadRequest, UserError::MissingToken)),
}
}
}
Environment
- rustc 1.55.0
- rocket 0.5.0-rc.1 with features
default
andjson
- sqlx 0.5.7 with features
runtime-tokio-native-tls
,all-types
,postgres
andmigrate