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
}
추천강좌
- Live Coding with Rust and Actix, Tensor Programming
- Développement Web Rust & Rocket, Thibaud Dauce
- A Basic Web Application with Rust and Actix-web, zupzup