logo MSJO.kr

Rust WebAPI using ACTIX

2020-06-21
MsJ

Rust 언어에 기반한 web framework로는 Actix, Rocket, Gotham 등이 있다. 이 중에서 최근에 많이 사용하는 Actix를 이용하여 basic 인증을 포함한 간단한 WebAPI 예제를 구현해보았다. 추가로 데이터베이스와 연동은 하단의 ‘추천강좌’와 ‘Reference’를 참고하자.

Cargo.toml
[package]
name = "hello_actix"
version = "0.1.0"
authors = ["DebugJO <me@msjo.kr>"]
edition = "2018"

[dependencies]
actix-rt = "1.1.1"
actix-web = "2.0.0"
actix-web-httpauth = "0.4.1"
serde = {version = "1.0.113", features = ["derive"]}
dotenv = "0.15.0"
config = "0.10.1"
환경설정파일 .env

root 폴더에 .env파일을 만들고 아래와 같이 설정파일을 작성한다.

SERVER.HOST=127.0.0.1
SERVER.PORT=80
config.rs
/* src/config.rs */
use config::ConfigError;
use serde::Deserialize;
use std::result::Result;

#[derive(Deserialize)]
pub struct ServerConfig {
    pub host: String,
    pub port: i32,
}

#[derive(Deserialize)]
pub struct Config {
    pub server: ServerConfig,
}

impl Config {
    pub fn from_env() -> Result<Self, ConfigError> {
        let mut cfg = config::Config::new();
        cfg.merge(config::Environment::new())?;
        cfg.try_into()
    }
}
models.rs
/* src/models.rs */
use serde::Serialize;

#[derive(Serialize)]
pub struct Student {
    pub id: String,
    pub name: String,
    pub email: String,
}
main.rs
/* src/main.rs */
mod config;
mod models;

use crate::models::Student;

use actix_web::{dev::ServiceRequest, web, App, Error, HttpServer, Responder};

use actix_web_httpauth::extractors::basic::{BasicAuth, Config};
use actix_web_httpauth::extractors::AuthenticationError;
use actix_web_httpauth::middleware::HttpAuthentication;

use dotenv::dotenv;
use std::io;

fn validate_credentials(user_id: &str, user_password: &str) -> Result<bool, std::io::Error> {
    // Basic Auth (Username, Password)
    if user_id.eq("abc") && user_password.eq("123") {
        return Ok(true);
    }
    return Err(std::io::Error::new(std::io::ErrorKind::Other, "Authentication failed!"));
}

async fn basic_auth_validator(req: ServiceRequest, credentials: BasicAuth) -> Result<ServiceRequest, Error> {
    let config = req.app_data::<Config>().map(|data| data.get_ref().clone()).unwrap_or_else(Default::default);
    match validate_credentials(credentials.user_id(), credentials.password().unwrap().trim()) {
        Ok(res) => {
            if res == true {
                Ok(req)
            } else {
                Err(AuthenticationError::from(config).into())
            }
        }
        Err(_) => Err(AuthenticationError::from(config).into()),
    }
}

async fn student() -> impl Responder {
    web::HttpResponse::Ok().json(Student {
        id: "1000".to_string(),
        name: "홍길동".to_string(),
        email: "1000@gmail.com".to_string(),
    })
}

#[actix_rt::main]
async fn main() -> io::Result<()> {
    dotenv().ok();

    let config = crate::config::Config::from_env().unwrap();

    println!("Staring server at http://{}:{}/", config.server.host, config.server.port);

    HttpServer::new(|| {
        let auth = HttpAuthentication::basic(basic_auth_validator);
        App::new().wrap(auth).route("/", web::get().to(student))
    })
    .bind(format!("{}:{}", config.server.host, config.server.port))?
    .run()
    .await
}
추천강좌
Reference

Prεv   Nεxt
Content
Search     RSS Feed     BY-NC-ND