From dcd66933aa7ed53ffa98f51b27afe572771cde8c Mon Sep 17 00:00:00 2001 From: xfy Date: Fri, 8 Nov 2024 17:04:40 +0800 Subject: [PATCH] feat: add user registry add hash password --- frameworks/Rust/axum/Cargo.lock | 1 + frameworks/Rust/axum/Cargo.toml | 1 + frameworks/Rust/axum/src/error.rs | 15 ++++-- frameworks/Rust/axum/src/routes/user.rs | 26 ++++++++-- frameworks/Rust/axum/src/utils/mod.rs | 1 + frameworks/Rust/axum/src/utils/validator.rs | 57 +++++++++++++++++++++ 6 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 frameworks/Rust/axum/src/utils/validator.rs diff --git a/frameworks/Rust/axum/Cargo.lock b/frameworks/Rust/axum/Cargo.lock index c087ebe..645049a 100644 --- a/frameworks/Rust/axum/Cargo.lock +++ b/frameworks/Rust/axum/Cargo.lock @@ -958,6 +958,7 @@ dependencies = [ "dotenvy", "jsonwebtoken", "rand", + "regex", "serde", "serde_json", "serde_repr", diff --git a/frameworks/Rust/axum/Cargo.toml b/frameworks/Rust/axum/Cargo.toml index 1685188..105cd63 100644 --- a/frameworks/Rust/axum/Cargo.toml +++ b/frameworks/Rust/axum/Cargo.toml @@ -18,6 +18,7 @@ thiserror = "2.0.0" dotenvy = "0.15.7" argon2 = "0.5.3" rand = "0.8.5" +regex = "1.11.1" serde = { version = "1.0.214", features = ["derive", "serde_derive"] } serde_json = { version = "1.0.132" } serde_repr = "0.1.19" diff --git a/frameworks/Rust/axum/src/error.rs b/frameworks/Rust/axum/src/error.rs index b92b11d..3c3a9fe 100644 --- a/frameworks/Rust/axum/src/error.rs +++ b/frameworks/Rust/axum/src/error.rs @@ -15,6 +15,8 @@ pub enum AppError { #[error("{0}")] Any(#[from] anyhow::Error), + #[error(transparent)] + ValidationError(#[from] validator::ValidationErrors), // axum #[error(transparent)] AxumFormRejection(#[from] FormRejection), @@ -80,11 +82,14 @@ impl IntoResponse for AppError { ParameterIncorrect, self.to_string(), ), - // route - // AppError::AuthorizeFailed(err) => { - // (StatusCode::UNAUTHORIZED, AuthorizeFailed, err.to_string()) - // } - // AppError::UserConflict(err) => (StatusCode::CONFLICT, UserConflict, err.to_string()), + AppError::ValidationError(_) => { + let message = format!("Input validation error: [{self}]").replace('\n', ", "); + (StatusCode::BAD_REQUEST, ParameterIncorrect, message) + } // route + // AppError::AuthorizeFailed(err) => { + // (StatusCode::UNAUTHORIZED, AuthorizeFailed, err.to_string()) + // } + // AppError::UserConflict(err) => (StatusCode::CONFLICT, UserConflict, err.to_string()), }; let body = Json(json!({ "code": code, diff --git a/frameworks/Rust/axum/src/routes/user.rs b/frameworks/Rust/axum/src/routes/user.rs index b15ad3e..79a1c49 100644 --- a/frameworks/Rust/axum/src/routes/user.rs +++ b/frameworks/Rust/axum/src/routes/user.rs @@ -1,12 +1,22 @@ +use crate::utils::password::hash; use axum::{routing::post, Json, Router}; use serde::{Deserialize, Serialize}; +use validator::Validate; + +use crate::utils::validator::EMAIL_REGEX; use super::{RouteResponse, RouteResult}; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Validate)] pub struct UserResigtry { + #[validate(length(min = 1, message = "Can not be empty"))] pub username: String, + #[validate(regex( + path = *EMAIL_REGEX, + message = "邮箱格式不正确" + ))] pub email: String, + #[validate(length(min = 6, max = 100, message = "Can not be empty"))] pub password: String, } @@ -14,13 +24,23 @@ pub struct UserResigtry { pub struct UserResigtryRes { pub username: String, pub email: String, + pub password: String, pub token: String, } pub async fn registry(Json(user_param): Json) -> RouteResult { + let UserResigtry { + email, + password, + username, + } = user_param; + + let hashed = hash(password).await?; + let data = UserResigtryRes { - username: "xfy".to_string(), - email: "i@rua.plus".to_string(), + username, + email, + password: hashed, token: "abc".to_string(), }; let res = RouteResponse { diff --git a/frameworks/Rust/axum/src/utils/mod.rs b/frameworks/Rust/axum/src/utils/mod.rs index e1230f7..a90f414 100644 --- a/frameworks/Rust/axum/src/utils/mod.rs +++ b/frameworks/Rust/axum/src/utils/mod.rs @@ -3,6 +3,7 @@ use tracing_subscriber::{fmt, prelude::*, registry, EnvFilter}; pub mod jwt; pub mod password; +pub mod validator; /// Initializes the logger for tracing. pub fn init_logger() { diff --git a/frameworks/Rust/axum/src/utils/validator.rs b/frameworks/Rust/axum/src/utils/validator.rs new file mode 100644 index 0000000..c6203e6 --- /dev/null +++ b/frameworks/Rust/axum/src/utils/validator.rs @@ -0,0 +1,57 @@ +use std::sync::LazyLock; + +use axum::{ + async_trait, + extract::{ + rejection::{FormRejection, JsonRejection}, + FromRequest, Request, + }, + Form, Json, +}; +use regex::Regex; +use serde::de::DeserializeOwned; +use validator::Validate; + +use crate::error::AppError; + +#[derive(Debug, Clone, Copy, Default)] +pub struct ValidatedForm(pub T); + +#[async_trait] +impl FromRequest for ValidatedForm +where + T: DeserializeOwned + Validate, + S: Send + Sync, + Form: FromRequest, +{ + type Rejection = AppError; + + async fn from_request(req: Request, state: &S) -> Result { + let Form(value) = Form::::from_request(req, state).await?; + value.validate()?; + Ok(ValidatedForm(value)) + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct ValidatedJson(pub T); + +#[async_trait] +impl FromRequest for ValidatedJson +where + T: DeserializeOwned + Validate, + S: Send + Sync, + Json: FromRequest, +{ + type Rejection = AppError; + + async fn from_request(req: Request, state: &S) -> Result { + let Json(value) = Json::::from_request(req, state).await?; + value.validate()?; + Ok(ValidatedJson(value)) + } +} + +pub static EMAIL_REGEX: LazyLock = LazyLock::new(|| { + Regex::new(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$").expect("Regex is valid") +});