Search code examples
rustactix-webrust-diesel

Trait bound in Actix routes


I'm making an API that already works with a few entities. While that part compiles, runs and works okay, I'm having serious problems when adding users: Actix doesn't seem to like the two routes related to them (create_user and user_login). This is my route delcaration:

HttpServer::new(move || {
        App::new()
            .app_data(actix_web::web::Data::new(pool.clone()))
            // User class
            .route(
                "/create_user",
                web::post().to(users_handlers::create_user_handler),
            )
            .route(
                "/login",
                web::post().to(users_handlers::user_login_handler),
            )

And the error I have, is the following:

the trait bound `fn(actix_web::web::Json<LoginData>, Data<Pool<ConnectionManager<diesel::SqliteConnection>>>) -> HttpResponse {user_login_handler}: Handler<_>` is not satisfied
the trait `Handler<_>` is not implemented for `fn(actix_web::web::Json<LoginData>, Data<Pool<ConnectionManager<diesel::SqliteConnection>>>) -> HttpResponse {user_login_handler}`rustcClick for full compiler diagnostic
main.rs(48, 29): required by a bound introduced by this call
route.rs(211, 12): required by a bound in `Route::to`

This is the full code of usres_handlers.rs, where you can see the create_user and login_user functions:

use crate::{
    models::{User, LoginData},
    utils::{log, LogType},
};
use actix_web::{http::header::ContentType, web, HttpResponse};
use diesel::{r2d2::ConnectionManager, SqliteConnection};
mod users_dao;
pub type DbPool = r2d2::Pool<ConnectionManager<SqliteConnection>>;

/**
 * HTTP Handlers for the users API
 * This layer is responsible for handling the HTTP requests and responses,
 * keep log of the errors, and call the DAO layer to interact with the database.
 *
 */

pub fn create_user_handler(user_data: web::Json<User>, pool: web::Data<DbPool>) -> HttpResponse {
    match (validate_user(user_data)) {
        Ok(user) => {
            let result = users_dao::create_user(user_data, pool);
            match result {
                Ok(user) => HttpResponse::Ok()
                    .content_type(ContentType::json())
                    .json(&user),
                Err(err) => {
                    println!("create_user_handler, ERROR: {:#?}", err);
                    log(LogType::Error, err.to_string());
                    HttpResponse::InternalServerError()
                        .content_type(ContentType::json())
                        .body("{err: 'Unable to insert user into database'")
                }
            }
        }
        Err(err) => {
            log(LogType::Error, err.to_string());
            HttpResponse::BadRequest()
                .content_type(ContentType::json())
                .body("{err: 'Unable to validate user data'")
        }
    }
}

pub fn user_login_handler(
    user_data: web::Json<LoginData>,
    pool: web::Data<DbPool>
) -> HttpResponse {
    let result = users_dao::user_login(user_data, pool);

    match (result) {
        Ok(user) => {
            log(LogType::Info, format!("User {} logged in", user.username));
            HttpResponse::Ok()
                    .content_type(ContentType::json())
                    .json(&user)
        },
        Err(err) => {
            log(LogType::Error, err.to_string());
            HttpResponse::BadRequest()
                .content_type(ContentType::json())
                .body("{err: 'Wrong username or password'")
        }
    }
}

// Private functions

fn validate_user(user: web::Json<User>) -> Result<web::Json<User>, &'static str> {
    if user.into_inner().username.is_empty() {
        return Err("Username is required");
    }
    if user.into_inner().password.is_empty() {
        return Err("Password is required");
    }
    Ok(user)
}

What am I doing wrong here?


Solution

  • Maybe try and make both functions async? Handler requires the output of a function to implement the Future trait.