第一个Rust项目,边写边学习。
Requirements Linux Actix-Web Diesel MySQL Diesel 由于事先配置好了数据库,这里选择DB-First模式,配置好数据库连接后(环境变量或者dotenv),diesel setup
后使用diesel print-schema > src/schema.rs
,可直接生成schema.rs
文件。至于model.rs
文件,还是需要自己手动完成编写的,在gitter上面问了一下为什么不支持自动生成model,说是因为不知道你所需要编写的具体业务代码。(还是不够自动化呗。 业务Model意指需要自己拼接的DTO。 下面给一些Diesel
简单的CRUD语句样例供参考: 使用的是MySQL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 let new_user = NewUser{ username: String::from ("Rust_Test" ), password: String::from ("origin_password" ), }; diesel::insert_into (user) .values (&new_user) .execute (&connection) .expect ("Insert Failed" ); let result = user .filter (username.eq ("Rust_Test" )) .first::<User>(&connection) .expect ("Error loading User." ); let mut old_user = user.filter (username.eq ("Rust_Test" )) .first::<User>(&connection) .expect ("Find Failed." ); old_user.password = String::from ("new_password" ); diesel::update (user) .filter (username.eq (old_user.username.clone ())) .set (&old_user) .execute (&connection) .expect ("Update Failed." ); diesel::delete (user) .filter (username.eq ("Rust_Test" )) .execute (&connection) .expect ("Delete Failed." );
这里是可能有用的环境配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 table! { user (id) { id -> Bigint, username -> Varchar, password -> Varchar, } } #[derive(Queryable,Debug,AsChangeset)] #[table_name="user" ] pub struct User { pub id: i64 , pub username: String , pub password: String , } #[derive(Insertable)] #[table_name="user" ] pub struct NewUser { pub username: String , pub password: String , }
Actix-Web 作为性能屠榜的框架,刚写完的时候发现速度没有那么快,最后发现是diesel
没有使用r2d2
连接池,不过用了以后发现速度还是赶不上Gin
+GORM
的速度(平均15~25ms),Actix-Web
+Diesel
则是30~40ms,不过比SpringBoot(130ms+)开了连接池以后还快了不少…
配置diesel-r2d2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 use diesel::{ r2d2::{ConnectionManager, Pool, PoolError, PooledConnection}, }; type MysqlPool = Pool<ConnectionManager<MysqlConnection>>;type MysqlPooledConnection = PooledConnection<ConnectionManager<MysqlConnection>>;pub fn get_connection_pool () -> Result <MysqlPool, PoolError> { dotenv ().ok (); let database_url = env::var ("DATABASE_URL" ).expect ("DATABASE_URL must be set" ); let manager = ConnectionManager::<MysqlConnection>::new (&database_url); Pool::builder ().max_size (15 ).build (manager) } }
Log配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 use actix_web::middleware::Logger;use env_logger::Env;#[actix_rt::main] async fn main () -> std::io::Result <()> { use actix_web::{App, HttpServer}; env_logger::from_env (Env::default ().default_filter_or ("info" )).init (); HttpServer::new (|| { App::new () .wrap (Logger::default ()) .wrap (Logger::new ("%a %{User-Agent}i" )) }) .bind ("127.0.0.1:8088" )? .run () .await }
Handler配置
handler.rs
1 2 3 4 5 6 7 8 9 10 11 12 pub async fn get_all_user (pool: Data<MysqlPool>) -> Result <HttpResponse> { let connection = pool .get () .map_err (|e| HttpResponse::InternalServerError ().json (e.to_string ())) .unwrap (); let users = user::get_all (&connection).unwrap (); Ok (HttpResponse::Ok ().json (users)) }
model.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 pub struct User { ... } pub struct NewUser { ... } use diesel::{prelude::*, result::Error};pub fn get_all (connection: &MysqlPooledConnection) -> Result <Vec <User>, Error> { use crate::schema::user::dsl::*; let users = user.load::<User>(connection)?; Ok (users.into ()) }
配置HttpServer(包含Logger和JsonConfig配置)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 pub async fn server_setup () -> std::io::Result <()> { env_logger::from_env (Env::default ().default_filter_or ("info" )).init (); let pool = get_connection_pool ().unwrap_or_else (|_| panic! ("Get poor error" )); HttpServer::new (move || { App::new () .data (pool.clone ()) .wrap (Logger::default ()) .service ( web::scope ("/user" ) .app_data (web::Json::<User>::configure (|cfg| { cfg.error_handler (|err, _req| { error::InternalError::from_response ( err, HttpResponse::Conflict ().finish (), ) .into () }) })) .route ("/" , web::get ().to (user::get_all_user)), ) }) .bind ("127.0.0.1:8088" )? .run () .await }
需要注意的是,Actix-Web
中的Extrator
已经很好用了,如Path
、Json
,但Json
在使用时,需要使用appdata()
注入包含JsonConfig
配置的configure
,关于Error
和Auth
的内容暂时还没完善。