### todo - [*] 使用Extractor方式提取数据,包括Body、Query和Path - [ ] multipart/form-data的实现 - [*] 参考example中的jwt实现方式,移除context对extension的依赖?那么language-tag该怎么处理? - [ ] 参考rocket,移除参数的元组类型 - [ ] 模拟request_util能否避免涉及到的parts.clone()? - [-] 能否自定义实现宏路由 - [*] 定向路由的实现 - [ ] 支持多种method的路由 - [ ] 宏路由支持fallback等 ### note - 展开宏:cargo expand --manifest-path .\server\Cargo.toml controller::feedback_controller ### sql dynamic query https://github.com/launchbadge/sqlx/blob/main/FAQ.md#how-can-i-bind-an-array-to-a-values-clause-how-can-i-do-bulk-inserts ```rust use sqlx::{query_builder::QueryBuilder, Execute}; struct Search { id: i64, username: Option, min_age: Option, max_age: Option, } fn search_query(search: Search) -> String { let mut query = QueryBuilder::new("SELECT * from users where id = "); query.push_bind(search.id); if let Some(username) = search.username { query.push(" AND username = "); query.push_bind(username); } if let Some(min_age) = search.min_age { query.push(" AND age > "); query.push_bind(min_age); } if let Some(max_age) = search.max_age { query.push(" AND age < "); query.push_bind(max_age); } query.build().sql().into() } fn main() { dbg!(search_query(Search { id: 12, username: None, min_age: None, max_age: None, })); // "SELECT * from users where id = $1" dbg!(search_query(Search { id: 12, username: Some("Bob".into()), min_age: None, max_age: None, })); // "SELECT * from users where id = $1 AND username = $2" dbg!(search_query(Search { id: 12, username: Some("Bob".into()), min_age: Some(10), max_age: Some(70), })); // "SELECT * from users where id = $1 AND username = $2 AND age > $3 AND age < $4" } ``` ```rust use sqlx::{Execute, MySql, QueryBuilder}; struct User { id: i32, username: String, email: String, password: String, } // The number of parameters in MySQL must fit in a `u16`. const BIND_LIMIT: usize = 65535; // This would normally produce values forever! let users = (0..).map(|i| User { id: i, username: format!("test_user_{i}"), email: format!("test-user-{i}@example.com"), password: format!("Test!User@Password#{i}"), }); let mut query_builder: QueryBuilder = QueryBuilder::new( // Note the trailing space; most calls to `QueryBuilder` don't automatically insert // spaces as that might interfere with identifiers or quoted strings where exact // values may matter. "INSERT INTO users(id, username, email, password) " ); // Note that `.into_iter()` wasn't needed here since `users` is already an iterator. query_builder.push_values(users.take(BIND_LIMIT / 4), |mut b, user| { // If you wanted to bind these by-reference instead of by-value, // you'd need an iterator that yields references that live as long as `query_builder`, // e.g. collect it to a `Vec` first. b.push_bind(user.id) .push_bind(user.username) .push_bind(user.email) .push_bind(user.password); }); let mut query = query_builder.build(); // You can then call `query.execute()`, `.fetch_one()`, `.fetch_all()`, etc. // For the sake of demonstration though, we're just going to assert the contents // of the query. // These are methods of the `Execute` trait, not normally meant to be called in user code. let sql = query.sql(); let arguments = query.take_arguments().unwrap(); assert!(sql.starts_with( "INSERT INTO users(id, username, email, password) VALUES (?, ?, ?, ?), (?, ?, ?, ?)" )); assert!(sql.ends_with("(?, ?, ?, ?)")); // Not a normally exposed function, only used for this doctest. // 65535 / 4 = 16383 (rounded down) // 16383 * 4 = 65532 assert_eq!(arguments.len(), 65532); ```