-
Rust 백엔드: REST API (Rocket + MySQL)컴퓨터/Rust 2021. 7. 14. 14:11728x90반응형
Rocket
Rust 언어용 웹 프레임워크
Rust Rocket 기본 설정
프로젝트 시작
cargo new rustweb --bin cd rustweb
기본 src/main.rs
#[macro_use] extern crate rocket; #[get("/")] fn index() -> &'static str { "Hello, world!" } #[launch] fn rocket() -> _ { rocket::build().mount("/", routes![index]) }
cargo build cargo run
GET localhost:8000/ 해보면 "Hello, world!"를 볼 수 있다.
Cargo.toml 수정
[dependencies] rocket = "0.4.10" rocket_contrib = "0.4" serde = { version = "1.0.126", features = ["derive"] } serde_json = "1.0.64" serde_derive = "1.0.126" reqwest = { version = "0.11", features = ["json"] } tokio = { version = "1", features = ["full"] } diesel = { version = "1.4.7", features = ["mysql"] } dotenv = "0.15.0" r2d2 = "*" r2d2-diesel = "*"
완성할 프로젝트 구조
(db 따로, route 따로, test 따로)
lib.rs에는 프로젝트 공통으로 쓰일 것들 모임
│ .env │ .gitignore │ Cargo.lock │ Cargo.toml │ disel.toml │ Makefile │ README.md │ Rocket.toml │ ├─src │ │ lib.rs │ │ main.rs │ │ │ ├─db │ │ connection.rs │ │ mod.rs │ │ models.rs │ │ query.rs │ │ schema.rs │ │ │ └─routes │ mod.rs │ notice.rs │ └─tests common.rs notice.rs parse.rs
MySQL 설정하기
Python, Go언어로 REST API를 만들 때보다 시간이 많이 걸렸다.
서버 주소는 숨기기 위해 Rocket.toml이나 .env에 저장해서 사용하면 된다.
Rocket.toml
[global.databases.my_db] url = "mysql://id:password@server/자신db"
.env
DATABASE_URL=mysql://id:password@server/자신db
lib.rs
#![feature(proc_macro_hygiene, decl_macro)] #[macro_use] extern crate rocket; #[macro_use] extern crate rocket_contrib; #[macro_use] extern crate diesel; // #[macro_use] // extern crate diesel_codegen; extern crate r2d2; extern crate r2d2_diesel; #[macro_use] extern crate serde_derive; mod db; mod routes; pub fn rocket() -> rocket::Rocket { rocket::ignite().manage(db::connection::init_pool()).mount( "/api", routes![routes::notice::hello, routes::notice::db_test], ) }
src/routes/notice.rs
/hello는 struct를 JSON으로 보내는 방법이고
/db는 db 데이터를 잘 받는지 테스트하는 endpoint이다.
use crate::db::connection::Conn; use crate::db::models::Schedule; use crate::db::query; use diesel; use diesel::result::Error; use rocket::http::Status; use rocket_contrib::json::Json; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] pub struct Notice { id: u64, title: String, date: String, link: String, writer: String, } #[get("/hello")] pub fn hello() -> Json<Notice> { let notice = Notice { id: 12345, title: "공지1".to_string(), date: "2021-07-09".to_string(), link: "https://".to_string(), writer: "CSW".to_string(), }; Json(notice) } #[get("/db")] pub fn db_test(conn: Conn) -> Result<Json<Vec<Schedule>>, Status> { let result = query::show_scheds(&conn) .map(|sched| Json(sched)) .map_err(|error| error_status(error)); for row in query::show_scheds(&conn).unwrap() { println!("id: {}, content: {}", row.id, row.content); } result } fn error_status(error: Error) -> Status { match error { Error::NotFound => Status::Ok, // 챗봇은 무조건 200 _ => Status::InternalServerError, } }
src/db/schema.rs
diesel::table! { ajou_sched (id) { id -> Integer, start_date -> Varchar, end_date -> Varchar, content -> Varchar, } }
src/db/query.rs
#![allow(proc_macro_derive_resolution_fallback)] use diesel; use diesel::prelude::*; use crate::db::models::Schedule; use crate::db::schema::ajou_sched::dsl::*; // SELECT * from ajou_sched limit 5 pub fn show_scheds(connection: &MysqlConnection) -> QueryResult<Vec<Schedule>> { //posts.filter(published.eq(true)) ajou_sched.limit(5).load::<Schedule>(&*connection) }
src/db/models.rs
#![allow(proc_macro_derive_resolution_fallback)] use crate::db::schema::ajou_sched; use diesel::prelude::*; #[derive(Queryable, AsChangeset, Serialize, Deserialize, Debug)] #[table_name = "ajou_sched"] pub struct Schedule { pub id: i32, pub content: String, pub start_date: String, pub end_date: String, } // impl Schedule { // pub fn read(conn: &MysqlConnection) -> Result<Vec<Schedule>, Error> { // ajou_sched::table // .order(ajou_sched::id.asc()) // .load::<Schedule>(conn) // } // }
src/db/connection.rs
MySQL pool을 이용해서 연결을 생성하는 방법이다.
(.env 파일에서 데이터 베이스 주소 읽어왔음)
use std::ops::Deref; use r2d2; use r2d2::PooledConnection; //use diesel::mysql::MysqlConnection; use diesel::mysql::*; use r2d2_diesel::ConnectionManager; use dotenv::dotenv; use rocket::http::Status; use rocket::request::{self, FromRequest}; use rocket::{Outcome, Request, State}; use std::env; pub type Pool = r2d2::Pool<ConnectionManager<MysqlConnection>>; // pub const DATABASE_FILE: &'static str = env!("DATABASE_URL"); pub fn init_pool() -> Pool { dotenv().ok(); // Grabbing ENV vars // Pull DATABASE_URL env var let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); // let config = Config::default(); let manager = ConnectionManager::<MysqlConnection>::new(database_url); Pool::new(manager).expect("db pool") } // #[derive(ConnectionPool)] pub struct Conn(pub PooledConnection<ConnectionManager<MysqlConnection>>); impl Deref for Conn { type Target = MysqlConnection; // #[inline(always)] fn deref(&self) -> &Self::Target { &self.0 } } impl<'a, 'r> FromRequest<'a, 'r> for Conn { type Error = (); fn from_request(request: &'a Request<'r>) -> request::Outcome<Conn, Self::Error> { let pool = request.guard::<State<Pool>>()?; match pool.get() { Ok(conn) => Outcome::Success(Conn(conn)), Err(_) => Outcome::Failure((Status::ServiceUnavailable, ())), } } }
결과
참고
아래 개인 프로젝트나 MySQL 연동 commit 참고
728x90'컴퓨터 > Rust' 카테고리의 다른 글
Rust: Remove duplicate strs in String (0) 2021.07.15 could not find native static library mysqlclient 오류 해결법 (0) 2021.07.10 Rust 문법: dyn, trait, polymorphism (0) 2021.05.02