-
SummaryHello everyone, I have a problem here that I can't solve. I have used gpt4 and consulted others but I have not been able to find the specific solution. I checked discussions and issues and found similar ones, but it still exists after the transformation. The initial error was caused by this error report. the trait bound `fn(Extension<AppState>, axum::Json<CodeReq>) -> impl Future<Output = impl IntoResponse> {send_code}: Handler<_, _>` is not satisfied
the following other types implement trait `Handler<T, S>`:
<axum::handler::Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>rustcClick for full compiler diagnostic
auth.rs(8, 25): required by a bound introduced by this call
method_routing.rs(140, 16): required by a bound in `post` I tried modifying the type returned by the handler and other methods. I also had concerns about the life cycle of sqlx and tried the with_state solution, but it didn't work and an error would be reported by the compiler. This is the initial code // main.rs
use axum::{Extension, Router};
mod common;
mod handlers;
mod routes;
mod services;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// app state
let state = common::app_state::get_state().await;
let app = Router::new()
.nest("/auth", routes::auth::auth_route())
.layer(Extension(state));
// .with_state(state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, Router::new().nest("/api", app)).await?;
Ok(())
}
// routes/auth.rs
use axum::{routing::post, Router};
use crate::handlers::auth;
pub fn auth_route() -> Router {
Router::new()
.route("/code", post(auth::send_code))
}
// handlers/auth.rs
use axum::{extract::Json, http::StatusCode, response::IntoResponse, Extension};
use serde::{Deserialize, Serialize};
use crate::common::{app_state::AppState, response::SuccessResponse};
use crate::services::sms::sms_send_code;
pub async fn send_code(
Extension(state): Extension<AppState>,
Json(payload): Json<CodeReq>,
) -> impl IntoResponse {
println!("payload: {:?}", payload.phone);
let phone = payload.phone;
match sms_send_code(&state.db, &phone).await {
Ok(_) => SuccessResponse {
code: StatusCode::OK.as_u16(),
message: "SMS verification code sent successfully.".to_string(),
data: (),
}
.into_response(),
Err(e) => e.into_response(),
}
}
#[derive(Deserialize)]
pub struct CodeReq {
phone: String,
}
// common/app_state.rs
use sqlx::{postgres::PgPoolOptions, PgPool};
#[derive(Clone)]
pub struct AppState {
pub db: PgPool,
}
pub async fn get_state() -> AppState {
let durl = "postgresql://postgres:postgres@localhost:5432/postgres";
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(&durl)
.await
.expect("unable to connect to database");
let state = AppState { db: pool };
state
}
// common/response.rs
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use serde::Serialize;
use serde_json::json;
use sqlx::Error as SqlxError;
#[derive(Serialize)]
pub struct ErrorResponse {
pub code: u16,
pub message: String,
}
#[derive(Debug)]
pub(crate) enum MyError {
SqlxError(SqlxError),
NormalError(String),
}
#[derive(Serialize)]
pub struct SuccessResponse<T> {
pub code: u16,
pub message: String,
pub data: T,
}
impl<T> IntoResponse for SuccessResponse<T>
where
T: Serialize,
{
fn into_response(self) -> Response {
let body = Json(self);
(StatusCode::OK, body).into_response()
}
}
impl IntoResponse for MyError {
fn into_response(self) -> Response {
let (status, error_message) = match self {
MyError::SqlxError(_) => (
StatusCode::INTERNAL_SERVER_ERROR,
"Database operation failed.",
),
MyError::NormalError(_) => (StatusCode::INTERNAL_SERVER_ERROR, "NormalError failed."),
};
let body = Json(json!({
"code": status.as_u16(),
"message": error_message,
}));
(status, body).into_response()
}
}
// services/sms.rs
use rand::Rng;
use sqlx::PgPool;
use crate::common::response::MyError;
pub async fn sms_send_code(pool: &PgPool, phone: &str) -> Result<bool, MyError> {
let mut rng = rand::thread_rng();
let code: u32 = rng.gen_range(100000..=999999);
let code_str = code.to_string();
println!(
"Sending SMS verification code: {} to phone: {}",
code_str, phone
);
sqlx::query("INSERT INTO SmsCode (code, phone, active) VALUES ($1, $2, $3)")
.bind(&code_str)
.bind(phone)
.bind(true)
.execute(pool)
.await
.map_err(MyError::SqlxError)?;
Ok(true)
}
// cargo.toml
[package]
name = "beapp"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = { version = "0.7.5", features = ["macros"] }
rand = "0.8.5"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "json"] }
tokio = { version = "1", features = ["full"] } According to a discussion in the forum about changing Extension to with_state, an error message was reported after the modification.
I also used I'm here to trouble you all. Sorry to bother everyone. Many thanks! axum version0.7.5 |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
try adding Can't really tell what you did wrong with the state, but I'd guess you added routes after the state. You need to first add all routes (that use a given state) and then add that state. |
Beta Was this translation helpful? Give feedback.
So it was becuase you held
let mut rng = rand::thread_rng();
across an await point, right? In that case you could also justdrop(rng)
after you generate the number and before theawait
. But using another generator is also valid.