# Rust Web Rokcet 系列 2 连接数据库 和 CURD 增删改查 使用的是 SQLite 数据库,实际上各种数据库都大同小异。已经有很多包帮助我们处理各种细节。目前 diesel 支持mysql,postgres和sqlite。*函数使用方法一般都在鼠标移动到函数上,显示出的文档中。换句话说,在 Ctrl+左键 点击进去显示的源文件的字里行间之中。* 相关文档: https://rocket.rs/v0.5-rc/guide/state/#databases 参考写法: https://github.com/SergioBenitez/Rocket/tree/v0.5-rc/examples/databases ## 安装相关工具 SQLite 和 diesel_cli 介绍一下 Windows SQLite 下安装方法 ,打开 SQLite 官网 的下载页。 https://www.sqlite.org/download.html 选择**Precompiled Binaries for Windows** 64位机下载[sqlite-dll-win64-x64-3370200.zip](https://www.sqlite.org/2022/sqlite-dll-win64-x64-3370200.zip) 和[sqlite-tools-win32-x86-3370200.zip](https://www.sqlite.org/2022/sqlite-tools-win32-x86-3370200.zip) 32位机下载[sqlite-dll-win32-x86-3370200.zip](https://www.sqlite.org/2022/sqlite-dll-win32-x86-3370200.zip)和[sqlite-tools-win32-x86-3370200.zip](https://www.sqlite.org/2022/sqlite-tools-win32-x86-3370200.zip) 解压这两个压缩包所有文件到一个目录,并使用 微软的 生成工具 ,在目录下执行 ```powershell lib /def:sqlite3.def /machine:X64 /out:sqlite3.lib ``` 然后将目录加入环境变量,然后打开终端执行: ```powershell cargo install diesel_cli --no-default-features --features sqlite ``` ## 编写 SQL 打开 项目目录,执行 ```powershell diesel setup diesel migration generate article ``` 生成了migrations\2022-02-14-114903_article\up.sql和migrations\2022-02-14-114903_article\down.sql 然后写 up.sql 和 down.sql migrations\2022-02-11-063903_product\up.sql ```sql -- Your SQL goes here CREATE TABLE article ( id INTEGER NOT NULL PRIMARY KEY, title Text NOT NULL, author Text NOT NULL, content Text NOT NULL, created_at Text NOT NULL ) ``` migrations\2022-02-14-114903_article\down.sql ```sql -- This file should undo anything in `up.sql` DROP TABLE article; ``` 创建项目根目录,和Cargo.toml同级。创建 .env 文件。写入 .env ``` DATABASE_URL=article.db ``` 然后执行 ```powershell diesel migration run ``` diesel会自动创建一个article.db,这就是我们之后会使用到的数据库了。除此之外,还有src\schema.rs和 diesel.toml文件。 使用数据库工具,可以看到已经成功创建了一个表 {{< image src="https://cdn.ftls.xyz/images/2022/02/20220214200151.png" caption="创建数据表" >}} ## 添加依赖 在Cargo.toml中添加依赖 Cargo.toml ```toml [package] name = "teach_used" version = "0.1.0" edition = "2021" authors = ["Kkbt <0@ftls.xyz>"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] rocket = { version = "0.5.0-rc.1",features = ["json"]} diesel = "1.4.8" [dependencies.rocket_sync_db_pools] version = "0.1.0-rc.1" default-features = false features = ["diesel_sqlite_pool"] ``` 创建Rocket.toml Rocket.toml ```toml [global.databases] sqlite_main = { url = "article.db" } ``` 然后在 main.rs 加入数据库连接,默认开启连接池。 ```rust #[macro_use] extern crate diesel; use rocket_sync_db_pools::database; #[database("sqlite_main")] pub struct MainDbConn(diesel::SqliteConnection); ``` 其实 rocket_sync_db_pools::diesel 也是可用的。类似于 ```rust use rocket_sync_db_pools::{database,diesel}; #[database("sqlite_main")] pub struct MainDbConn(diesel::SqliteConnection); ``` 但是我还没弄明白如何导入 `#[macro_use]` ,这个宏在 src\schema.rs 中 table 用到了。有人知道的话知会我一声。这样的应该不用引入 diesel = "1.4.8" 依赖了。 ## CURD 然后修改 src\main.rs,src\module.rs,src\routes.rs 。 src\main.rs : ```rust #[macro_use] extern crate rocket; #[macro_use] extern crate diesel; mod module; mod routes; mod schema; use rocket_sync_db_pools::database; use routes::*; #[database("sqlite_main")] pub struct MainDbConn(diesel::SqliteConnection); #[launch] fn rocket() -> _ { rocket::build() // database .attach(MainDbConn::fairing()) .mount("/", routes![index]) // add api .mount("/", routes![get_all_articles, get_article_by_id]) .mount("/", routes![post_article, put_article]) .mount("/", routes![delete_all_articles, delete_article]) } ``` src\module.rs ```rust use crate::schema::article; use diesel::Insertable; use rocket::serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug, Queryable, Insertable)] #[serde(crate = "rocket::serde")] #[table_name = "article"] pub struct Article { pub id: i32, pub title: String, pub author: String, pub content: String, pub created_at: String, } #[derive(Debug, Serialize, Deserialize, Queryable, Clone, Insertable, AsChangeset)] #[serde(crate = "rocket::serde")] #[table_name = "article"] pub struct PostArticle { pub title: String, pub author: String, pub content: String, pub created_at: String, } ``` src\routes.rs 简单写了 CURD ,具体可以看文档。函数使用方法一般都在鼠标移动到函数上,显示出的文档中。换句话说,在 Ctrl+左键 点击进去显示的源文件的字里行间之中。 ```rust use diesel::{prelude::*, QueryDsl, RunQueryDsl}; use rocket::serde::json::{serde_json::json, Json, Value}; use rocket::{delete, get, post, put, response::status::Created, response::Debug}; use crate::module::{Article, PostArticle}; use crate::schema::article; use crate::MainDbConn; type Result> = std::result::Result; #[get("/")] pub fn index() -> Value { json!({"kkbt":"Hello, world!"}) } // 查 all #[get("/article")] pub async fn get_all_articles(db: MainDbConn) -> Result>> { let all = db .run(move |conn| article::table.load::
(conn)) .await?; Ok(Json(all)) } // 查 by id #[get("/article/")] pub async fn get_article_by_id(db: MainDbConn, in_id: i32) -> Option> { db.run(move |conn| article::table.filter(article::id.eq(in_id)).first(conn)) .await .map(Json) .ok() } // 增 #[post("/article", format = "json", data = "")] pub async fn post_article( db: MainDbConn, in_article: Json, ) -> Result>> { let article_in = in_article.clone(); db.run(move |conn| { diesel::insert_into(article::table) .values(&article_in) .execute(conn) }) .await?; Ok(Created::new("/").body(in_article)) } // 改 by id #[put("/article/", format = "json", data = "")] pub async fn put_article( db: MainDbConn, in_id: i32, in_article: Json, ) -> Result> { let affected = db .run(move |conn| { diesel::update(article::table.filter(article::id.eq(in_id))) .set(in_article.into_inner()) .execute(conn) }) .await?; Ok((affected == 1).then(|| ())) } // 删 by id #[delete("/article/")] pub async fn delete_article(db: MainDbConn, in_id: i32) -> Result> { let affected = db .run(move |conn| { diesel::delete(article::table) .filter(article::id.eq(&in_id)) .execute(conn) }) .await?; Ok((affected == 1).then(|| ())) } // 删 all #[delete("/article/all")] pub async fn delete_all_articles(db: MainDbConn) -> Result<()> { db.run(move |conn| diesel::delete(article::table).execute(conn)) .await?; Ok(()) } ``` 本系列 Postman 分享链接: https://www.getpostman.com/collections/c89ec512876818f18757 如果链接失效了,请只会一声。或 teach.postman_collection.json 如下 ```json { "info": { "_postman_id": "709bf03a-bdd4-4b98-afac-b01ae46d2b65", "name": "teach", "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json", "_exporter_id": "16599952" }, "item": [ { "name": "http://127.0.0.1:8000/", "request": { "method": "GET", "header": [], "url": "http://127.0.0.1:8000/" }, "response": [] }, { "name": "获取所有文章", "request": { "method": "GET", "header": [], "url": "http://127.0.0.1:8000/article" }, "response": [] }, { "name": "增加文章", "request": { "method": "POST", "header": [], "body": { "mode": "raw", "raw": "{\r\n \"title\": \"a title\",\r\n \"author\": \"恐咖兵糖\",\r\n \"content\": \"dasdaadas\",\r\n \"created_at\": \"2022-02-14 \"\r\n}", "options": { "raw": { "language": "json" } } }, "url": "http://127.0.0.1:8000/article" }, "response": [] }, { "name": "获取文章 by id", "request": { "method": "GET", "header": [], "url": "http://127.0.0.1:8000/article/2" }, "response": [] }, { "name": "删除文章 by id", "request": { "method": "DELETE", "header": [], "url": "http://127.0.0.1:8000/article/3" }, "response": [] }, { "name": "更新文章", "request": { "method": "PUT", "header": [], "body": { "mode": "raw", "raw": "{\r\n \"title\": \"a title\",\r\n \"author\": \"恐咖兵糖\",\r\n \"content\": \"222ssss2\",\r\n \"created_at\": \"2022-02-14 \"\r\n}", "options": { "raw": { "language": "json" } } }, "url": "http://127.0.0.1:8000/article/4" }, "response": [] }, { "name": "删除所有文章", "request": { "method": "DELETE", "header": [], "url": "http://127.0.0.1:8000/article/all" }, "response": [] }, { "name": "获取token", "request": { "method": "POST", "header": [], "body": { "mode": "raw", "raw": "{\r\n \"id\": 0,\r\n \"key\": \"oR66T*W8y4VaXkh#rTjeZ$$Rby$NCy!nJX\"\r\n}", "options": { "raw": { "language": "json" } } }, "url": "http://127.0.0.1:8000/token" }, "response": [] }, { "name": "token 测试", "event": [ { "listen": "prerequest", "script": { "exec": [ "const gettoken = {\r", " url: ' http://127.0.0.1:8000/token',\r", " method: \"POST\",\r", " header: 'Content-Type: application/json',\r", " body: {\r", " mode: 'raw', \r", " raw: JSON.stringify({\"id\": 0, \"key\": \"oR66T*W8y4VaXkh#rTjeZ$$Rby$NCy!nJX\"}) //要将JSON对象转为文本发送\r", "\r", " }\r", "}\r", "pm.sendRequest(gettoken, function (err, response) {\r", " console.log(response.json().token);\r", " pm.collectionVariables.set(\"token\",response.json().token);\r", "});" ], "type": "text/javascript" } } ], "request": { "method": "GET", "header": [ { "key": "Authorization", "value": "Bearer {{token}}", "type": "default" } ], "url": "http://127.0.0.1:8000/token/test" }, "response": [] } ], "variable": [ { "key": "token", "value": "" } ] } ```