ABOUT ME

-

Total
-
  • Rust: actix + MongoDB
    컴퓨터/Rust 2021. 8. 28. 09:59
    728x90
    반응형

    actix

     

    Actix Web | A powerful, pragmatic, and extremely fast web framework for Rust.

    Request Routing An actix app comes with a URL routing system that lets you match on URLs and invoke individual handlers. For extra flexibility, scopes can be used. #[get("/")] async fn index(_req: HttpRequest) -> impl Responder { "Hello from the index page

    actix.rs

     

    Actix + MongoDB

    NoSQL 중 Document 개념을 사용하는 MongoDB

    웹 프레임워크인 actix에 mongoDB를 사용하는 예제 (backend)

    mongoDB는 atlas에서 무료로 간단한 서버를 만들었다.

     

    Setup

    Cargo.toml

    De/Serialize 하기 위한 serde, futures는 try_next()란 cursor에서 iterate 하기 위해 필요함

    [dependencies]
    mongodb = "2.0.0-beta.3"
    actix-rt = "2"
    actix-http = "2"
    actix-web = "4.0.0-beta.8" 
    serde = { version = "1.0", features = ["derive"] }
    serde_json = "1.0"
    serde_derive = "1.0"
    futures = "0.3"

    src/lib.rs

    type Mongo를 엔드포인트 함수에 web::Data <Mongo>처럼 사용하면 된다.

    #![feature(proc_macro_hygiene, decl_macro)]
    
    #[macro_use]
    extern crate serde_derive;
    #[macro_use]
    extern crate serde_json;
    
    extern crate mongodb;
    
    use mongodb::Client;
    use std::sync::Mutex;
    pub type Mongo = Mutex<Client>;
    
    mod db;
    mod routes;
    
    pub use db::model;
    pub use routes::*;
    
    // mongo+srv//id:password~~~~
    pub const MONGO_URL: &str = env!("MONGODB_URL");
    pub const SERVER: &str = "0.0.0.0:8010";

     

    src/main.rs

    몽고 DB는 Mutex에 client를 만들어서 actix의 app_data에 clone 하면 된다.

    use actix_web::{middleware, web, App, HttpServer};
    use mongodb::{options::ClientOptions, Client};
    use std::sync::Mutex;
    
    #[actix_web::main]
    async fn main() -> std::io::Result<()> {
        // MongoDB Client
        let client_options = ClientOptions::parse(project::MONGO_URL).await.unwrap();
        let client = web::Data::new(Mutex::new(Client::with_options(client_options).unwrap()));
    
        // start http server
        HttpServer::new(move || {
            App::new()
                .app_data(client.clone()) // store db pool in app state
                .wrap(middleware::Logger::default())
                .service(project::hello::hello)
        })
        .bind(project::SERVER)?
        .run()
        .await
    }

     

    src/db/model.rs

    테스트는 대학교 공지를 이용할 것이다.

    MongoDB에 이미 아래 field처럼 공지 1650개를 저장해 두었다.

    MongoDB Compass

     

    #[derive(Serialize, Deserialize, Debug, Default, Clone)]
    pub struct Notice {
        pub id: i32,
        pub category: String,
        pub title: String,
        pub date: String,
        pub link: String,
        pub writer: String,
    }

     

    엔드포인트 예제

    src/routes/hello.rs

    이 예제는 GET /notice에 query를 같이 해서 보내면 필터링해서 공지를 json으로 return한다.

    ex) GET ~/notice?date=21.01.06&category=학사 (21년 1월 6월에 "학사" 공지를 불러옴)

    FindOptions에 limit, sort 같은 기능들이 있으니 @공식 문서 참고

    #![allow(proc_macro_derive_resolution_fallback)]
    
    use crate::db::model::Notice;
    use crate::Mongo;
    use actix_web::{get, web, HttpRequest, HttpResponse, Responder};
    use futures::stream::TryStreamExt;
    use mongodb::{bson::doc, options::FindOptions};
    use std::collections::HashMap;
    
    #[get("/notice")]
    pub async fn hello(db: web::Data<Mongo>, req: HttpRequest) -> impl Responder {
        let params = web::Query::<HashMap<String, String>>::from_query(req.query_string()).unwrap();
    
        // 아래와 같이 query를 보내면 Query struct엔 아래와 같이 Map으로 저장됨
        // http://localhost:8010/notice?date=21.01.06&id=4
        // Query({"id": "4", "date": "21.01.06"})
        // println!("{:?}", params);
    
        let typed_collection = db
            .lock()
            .unwrap()
            .database("ajou")
            .collection::<Notice>("notice");
    
        let date = params.get("date");
        let cate = params.get("category");
    
        
        // query 중 date, category 둘다 있는지 한 개만 있는지 체크해서 build
        let mut notices = if date.is_some() && cate.is_some() {
            typed_collection
                .find(
                    doc! {"$and" : [{ "date": { "$eq": date } }, { "category": { "$eq": cate }}]},
                    None,
                )
                // .find(doc! {"date": {"$eq": params.get("date")}}, None)
                .await
                .unwrap()
        } else if date.is_some() {
            typed_collection
                .find(doc! {"date": {"$eq": date}}, None)
                .await
                .unwrap()
        } else if cate.is_some() {
            typed_collection
                .find(doc! {"category": {"$eq": cate}}, None)
                .await
                .unwrap()
        } else {
            let find_options = FindOptions::builder().limit(1).build();
            typed_collection.find(doc! {}, find_options).await.unwrap()
        };
    
        let mut result: Vec<Notice> = Vec::new();
        while let Some(notice) = notices.try_next().await.unwrap() {
            result.push(notice);
        }
    
        HttpResponse::Ok()
            .content_type("application/json")
            .json(json!(result))
    }
    
    /* 결과
    GET: http://localhost:8010/notice?date=21.08.26&category=학사
    
    [
        {
            "id": 13987,
            "category": "학사",
            "title": "[봉사활동] 2021 하반기 영통구청 저소득층 언택트 멘토링 자원봉사자 모집(~8/31)",
            "date": "21.08.26",
            "link": "https://www.ajou.ac.kr/kr/ajou/notice.do?mode=view&articleNo=112670&article.offset=0&articleLimit=1648",
            "writer": "사회봉사센터"
        },
        {
            "id": 13988,
            "category": "학사",
            "title": "[다산학부대학] 2021학년도 2학기 신설 교과목 안내(기초수학A, 기초수학B)",
            "date": "21.08.26",
            "link": "https://www.ajou.ac.kr/kr/ajou/notice.do?mode=view&articleNo=112674&article.offset=0&articleLimit=1648",
            "writer": "다산학부대학교학팀"
        },
        {
            "id": 13992,
            "category": "학사",
            "title": "(추가공지)[다산학부대학] 2021-2학기 Co-BSM 본수강신청 안내",
            "date": "21.08.26",
            "link": "https://www.ajou.ac.kr/kr/ajou/notice.do?mode=view&articleNo=112687&article.offset=0&articleLimit=1648",
            "writer": "다산학부대학교학팀"
        }
    ]
    
     */

     

    728x90

    '컴퓨터 > Rust' 카테고리의 다른 글

    Rust: Cross compile to linux on windows  (0) 2021.08.30
    Rust: reqwest GET/POST snippet  (0) 2021.08.19
    Rust: actix를 이용하여 카카오 챗봇 만들기  (1) 2021.08.02

    댓글