From 94cb27d1e75bcb94a1ddd6794e686f441d70caeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E8=BF=90=E5=AE=B6?= Date: Mon, 30 Sep 2024 09:46:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BD=91=E7=BB=9C=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E4=B8=ADquery=E5=8F=82=E6=95=B0=E7=9A=84=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- i18n.csv | 5 +- i18n/src/message_ids.rs | 1 + library/src/lib.rs | 1 + library/src/middleware/req_ctx.rs | 16 +----- library/src/model/mod.rs | 3 +- library/src/model/query_validator.rs | 41 +++++++++++++++ library/src/utils/mod.rs | 1 + library/src/utils/request_util.rs | 20 ++++++++ server/src/controller/feedback_controller.rs | 3 +- server/src/controller/todo.rs | 54 ++++++++++++++++++++ 10 files changed, 127 insertions(+), 18 deletions(-) create mode 100644 library/src/model/query_validator.rs create mode 100644 library/src/utils/mod.rs create mode 100644 library/src/utils/request_util.rs create mode 100644 server/src/controller/todo.rs diff --git a/i18n.csv b/i18n.csv index bb4dc0e..e28b6c4 100644 --- a/i18n.csv +++ b/i18n.csv @@ -6,10 +6,11 @@ AccountNoPermission,account has no permission,账户无权限 IncorrectUsernameOrPassword,incorrect username or password,用户名或密码错误 InvalidToken,invalid token,无效令牌 ValidateFeedbackContentRequired,feedback content is required,反馈内容不能为空 -ValidateAccountNameRequired,username is required,"用户名称不能为空" +ValidateAccountNameRequired,username is required,用户名称不能为空 ValidateAccountPasswordRequired,password is required,密码不能为空 ValidateAccountIdTokenRequired,ID Token is required,用户ID Token不能为空 ValidateAccountLangTagRequired,lang tag is required,用户语言标识不能为空 ValidatePageablePageRequired,invalid page number,页码无效 ValidatePageablePageSizeRequired,invalid quantity per page,每页数量无效 -BadRequest,bad request,无效请求 \ No newline at end of file +BadRequest,bad request,无效请求 +InvalidParams,invalid params,无效参数 \ No newline at end of file diff --git a/i18n/src/message_ids.rs b/i18n/src/message_ids.rs index b99ba29..71f0a39 100644 --- a/i18n/src/message_ids.rs +++ b/i18n/src/message_ids.rs @@ -10,6 +10,7 @@ pub enum MessageId { AccountNoPermission, IncorrectUsernameOrPassword, InvalidToken, + InvalidParams, ValidateFeedbackContentRequired, ValidateAccountNameRequired, diff --git a/library/src/lib.rs b/library/src/lib.rs index afff00e..7a2a7f2 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -8,3 +8,4 @@ pub mod social; pub mod cache; pub mod context; pub mod task; +pub mod utils; \ No newline at end of file diff --git a/library/src/middleware/req_ctx.rs b/library/src/middleware/req_ctx.rs index 8ce2e21..3ff8df8 100644 --- a/library/src/middleware/req_ctx.rs +++ b/library/src/middleware/req_ctx.rs @@ -3,7 +3,7 @@ use http::{header, StatusCode}; use i18n::{message, message_ids::MessageId}; use jsonwebtoken::{decode, DecodingKey, Validation}; -use crate::{cache::account_cache::LOGIN_CACHE, config, context::{Context, WhiteContext}, model::response::ResErr, token::Claims}; +use crate::{cache::account_cache::LOGIN_CACHE, config, context::{Context, WhiteContext}, model::response::ResErr, token::Claims, utils::request_util}; const WHITE_LIST: &[(&str, &str)] = &[ ("POST", "/account/sys"), @@ -13,19 +13,7 @@ const WHITE_LIST: &[(&str, &str)] = &[ /// 认证中间件,包括网络请求白名单、token验证、登录缓存 pub async fn authenticate_ctx(mut req: Request, next: Next) -> Response { // 解析语言 - let mut language = String::from("zh-CN"); - let language_header = req.headers().get(header::ACCEPT_LANGUAGE); - language = match language_header { - Some(value) => { - let value_str: Vec<&str> = value.to_str().unwrap_or("zh-CN").split(',').collect(); - if value_str.is_empty() { - language - } else { - String::from(value_str[0]) - } - }, - None => language, - }; + let language = request_util::get_lang_tag(req.headers()); req.extensions_mut().insert(WhiteContext { lang_tag: language.clone() }); // 获取请求的url和method,然后判断是否在白名单中,如果在白名单中,则直接返回next(req),否则继续执行下面的代码 let method = req.method().clone().to_string(); diff --git a/library/src/model/mod.rs b/library/src/model/mod.rs index 0cf7839..4eda66d 100644 --- a/library/src/model/mod.rs +++ b/library/src/model/mod.rs @@ -1,2 +1,3 @@ pub mod response; -pub mod validator; \ No newline at end of file +pub mod validator; +pub mod query_validator; \ No newline at end of file diff --git a/library/src/model/query_validator.rs b/library/src/model/query_validator.rs new file mode 100644 index 0000000..6513fb8 --- /dev/null +++ b/library/src/model/query_validator.rs @@ -0,0 +1,41 @@ +use axum::{async_trait, body::Body, extract::{FromRequest, FromRequestParts, Query}}; +use http::Request; +use i18n::{message, message_ids::MessageId}; +use validator::Validate; + +use crate::utils::request_util; + +use super::response::ResErr; + +pub struct QueryValidator(pub T); + +#[async_trait] +impl FromRequest for QueryValidator +where + S: Send + Sync, + T: Validate, + Query: FromRequestParts, +{ + type Rejection = ResErr; + + async fn from_request(req: http::Request, state: &S) -> Result { + let (parts, body) = req.into_parts(); + let query = Query::::from_request(Request::from_parts(parts.clone(), body), state).await; + let header = &parts.headers; + + if let Ok(Query(data)) = query { + match data.validate() { + Ok(_) => Ok(QueryValidator(data)), + Err(_) => { + let lang_tag = request_util::get_lang_tag(header); + let err = Err(ResErr::params(message!(&lang_tag, MessageId::InvalidParams))); + err + }, + } + } else { + let lang_tag = request_util::get_lang_tag(header); + let err = Err(ResErr::params(message!(&lang_tag, MessageId::InvalidParams))); + err + } + } +} \ No newline at end of file diff --git a/library/src/utils/mod.rs b/library/src/utils/mod.rs new file mode 100644 index 0000000..180b857 --- /dev/null +++ b/library/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod request_util; \ No newline at end of file diff --git a/library/src/utils/request_util.rs b/library/src/utils/request_util.rs new file mode 100644 index 0000000..7ddc573 --- /dev/null +++ b/library/src/utils/request_util.rs @@ -0,0 +1,20 @@ +use axum::extract::Request; +use http::{header, HeaderMap, HeaderValue}; + +/// 获取请求的语言 +#[inline] +pub fn get_lang_tag(headers: &HeaderMap) -> String { + let language = String::from("zh-CN"); + let language_header = headers.get(header::ACCEPT_LANGUAGE); + match language_header { + Some(value) => { + let value_str: Vec<&str> = value.to_str().unwrap_or("zh-CN").split(',').collect(); + if value_str.is_empty() { + language + } else { + String::from(value_str[0]) + } + }, + None => language, + } +} \ No newline at end of file diff --git a/server/src/controller/feedback_controller.rs b/server/src/controller/feedback_controller.rs index 0d96b06..04f60a8 100644 --- a/server/src/controller/feedback_controller.rs +++ b/server/src/controller/feedback_controller.rs @@ -4,6 +4,7 @@ use domain::dto::feedback::FeedbackAdd; use domain::dto::pageable::PageParams; use domain::vo::feedback::FeedbackPageable; use library::context::Context; +use library::model::query_validator::QueryValidator; use library::model::response::ResResult; use library::model::validator; @@ -25,7 +26,7 @@ pub async fn add_feedback( /// 获取反馈信息列表 pub async fn get_feedback_list_by_page( Extension(context): Extension, - Query(page_params): Query, + QueryValidator(page_params): QueryValidator, ) -> ResResult { validator::validate_params(&page_params, context.get_lang_tag())?; service::feedback_service::get_feedback_list_by_page( diff --git a/server/src/controller/todo.rs b/server/src/controller/todo.rs new file mode 100644 index 0000000..c692447 --- /dev/null +++ b/server/src/controller/todo.rs @@ -0,0 +1,54 @@ +// https://github.com/tokio-rs/axum/discussions/2081 +// use axum::{ +// async_trait, +// extract::{FromRequest, FromRequestParts, Query}, +// http, response, +// }; +// use serde::{Deserialize, Serialize}; +// use validator::Validate; + +// pub struct Validated(pub T); + +// #[async_trait] +// impl FromRequest for Validated +// where +// S: Send + Sync, +// B: Send + 'static, +// T: Validate, +// Query: FromRequestParts, +// { +// type Rejection = (http::StatusCode, String); + +// async fn from_request(req: http::Request, state: &S) -> Result { +// let query = Query::::from_request(req, state).await; + +// if let Ok(Query(data)) = query { +// match data.validate() { +// Ok(_) => Ok(Validated(data)), +// Err(err) => Err((http::StatusCode::BAD_REQUEST, err.to_string())), +// } +// } else { +// Err(( +// http::StatusCode::INTERNAL_SERVER_ERROR, +// "internal server error".to_string(), +// )) +// } +// } +// } + +// // my handler +// #[derive(Deserialize, Serialize, Validate)] +// pub struct Pagination { +// #[validate(range(min = 1))] +// page: usize, + +// #[validate(range(max = 100))] +// per_page: usize, +// } + +// #[axum::debug_handler] +// pub async fn get_query_string( +// Validated(pagination): Validated, +// ) -> response::Json { +// response::Json(pagination) +// } \ No newline at end of file