I am currently trying to implement a web service, which is based on actix_web 4 and use actix_web_httpauth and jsonwebtoken for the JWT handling and sea_orm as a ORM for Postgres. My code looks similar to this howto. My problem is that my validator function needs to do a check if a specific entry in the database exists. So I have to pass the DatabaseConnection to the validator.
My database connection looks like this:
let db: DatabaseConnection = Database::connect(&config.database_url)
.await
.expect("Database error");
48 | let auth = HttpAuthentication::bearer(|req, cred| auth_token (req, cred, db.clone()));
| --------------------------------------------------------------------------
| | | |
| | | borrow occurs due to use in closure
| | borrow of `db` occurs here
| argument requires that `db` is borrowed for `'static`
49 | HttpServer::new(move || {
| ^^^^^^^ move out of `db` occurs here
...
53 | .app_data(actix_web::web::Data::new(db.clone()))
| -- move occurs due to use in closure
and I would"ve thought that ServiceRequest and BearerAuth are only available within HttpServer
Then you could apply the same logic for db and create the clone outside of the closure where the borrowing occurs. But as you will have to access the db connection in multiple places, then a better idea would be to use the fact that you pass it in an Arc (Data) in the app_data.
The middleware req parameter allows you to retrieve the app_data, like in any service:
let auth = HttpAuthentication::bearer(|req, cred| {
let db = req
.app_data::<actix_web::web::Data<DatabaseConnection>>()
.expect("Failed to extract DatabaseConnection from ServiceRequest");
auth_token (req, cred, db)
});
I couldn't test it because I don't have the context so maybe you'd have to adapt a little but I have done similar things already.
#[actix_web::main]
pub async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("debug"));
let config = get_config().await;
info!(
"Starting HTTP server at http://{}:{}",
&config.bind_addr, &config.bind_port
);
let db = Database::connect(&config.database_url)
.await
.expect("Database error");
let _ = migration::initialize_database(&db).await;
HttpServer::new(move || {
let auth = HttpAuthentication::bearer(|req, cred| {
let db = req
.app_data::<actix_web::web::Data<DatabaseConnection>>()
.expect("Failed to extract DatabaseConnection from ServiceRequest");
auth_token (req, cred, db.clone())
});
App::new()
...
})
.bind(format!("{}:{}", &config.bind_addr, &config.bind_port))?
.run()
.await
}
pub async fn auth_token(
req: ServiceRequest,
bearer: BearerAuth,
db: actix_web::web::Data<sea_orm::DatabaseConnection>,
) -> Result<ServiceRequest, (Error, ServiceRequest)> {
...
}
Error is:
error[E0505]: cannot move out of `req` because it is borrowed
--> server-api/src/lib.rs:58:25
|
55 | let db = req
| ______________________-
56 | | .app_data::<actix_web::web::Data<DatabaseConnection>>()
| |_______________________________________________________________________- borrow of `req` occurs here
57 | .expect("Failed to extract DatabaseConnection from ServiceRequest");
58 | auth_token (req, cred, db.clone())
| ^^^ ---------- borrow later used here
| |
| move out of `req` occurs here
let auth = HttpAuthentication::bearer(|req, cred| {
let db = req
.app_data::<actix_web::web::Data<DatabaseConnection>>()
.expect("Failed to extract DatabaseConnection from ServiceRequest")
.get_ref()
.clone();
auth_token(req, cred, db)
});
Now I can access the database within the validator function. Thank you very much for your support and help!!