Rust actix-web入門:Hello Worldからルーティングまで
はじめに
actix-webはRustで最もよく使われるWebフレームワークの1つです。高いパフォーマンスと豊富な機能を持ち、APIサーバーやWebアプリケーションの開発に使われています。
この記事では actix-web 4.x を使って、プロジェクト作成からルーティング・パラメータ取得・JSONレスポンスまでを解説します。
プロジェクトの作成
新しいRustプロジェクトを作成します。
cargo new actix-hello cd actix-hello
Cargo.toml に依存関係を追加します。
[dependencies] actix-web = "4" tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1"
serde はJSON変換のために使います。
Hello Worldを動かす
src/main.rs を以下のように書き換えます。
use actix_web::{get, App, HttpResponse, HttpServer, Responder}; #[get("/")] async fn hello() -> impl Responder { HttpResponse::Ok().body("Hello, World!") } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .service(hello) }) .bind("127.0.0.1:8080")? .run() .await }
サーバーを起動します。
cargo run
別のターミナルで動作を確認します。
curl http://127.0.0.1:8080/ # Hello, World!
ルーティング
複数のパスとHTTPメソッドを登録できます。
use actix_web::{delete, get, post, put, App, HttpResponse, HttpServer, Responder}; #[get("/users")] async fn get_users() -> impl Responder { HttpResponse::Ok().body("ユーザー一覧") } #[post("/users")] async fn create_user() -> impl Responder { HttpResponse::Created().body("ユーザー作成") } #[put("/users/{id}")] async fn update_user() -> impl Responder { HttpResponse::Ok().body("ユーザー更新") } #[delete("/users/{id}")] async fn delete_user() -> impl Responder { HttpResponse::Ok().body("ユーザー削除") } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .service(get_users) .service(create_user) .service(update_user) .service(delete_user) }) .bind("127.0.0.1:8080")? .run() .await }
パスパラメータを取得する
URLの一部を変数として受け取るには web::Path を使います。
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder}; #[get("/users/{id}")] async fn get_user(path: web::Path<u32>) -> impl Responder { let id = path.into_inner(); HttpResponse::Ok().body(format!("ユーザーID: {}", id)) } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new().service(get_user) }) .bind("127.0.0.1:8080")? .run() .await }
curl http://127.0.0.1:8080/users/42 # ユーザーID: 42
複数のパスパラメータを受け取る場合はタプルを使います。
#[get("/users/{user_id}/posts/{post_id}")] async fn get_post(path: web::Path<(u32, u32)>) -> impl Responder { let (user_id, post_id) = path.into_inner(); HttpResponse::Ok().body(format!("ユーザー{}の投稿{}", user_id, post_id)) }
クエリパラメータを取得する
?key=value 形式のクエリパラメータは web::Query で受け取ります。
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder}; use serde::Deserialize; #[derive(Deserialize)] struct SearchQuery { q: String, limit: Option<u32>, } #[get("/search")] async fn search(query: web::Query<SearchQuery>) -> impl Responder { let limit = query.limit.unwrap_or(10); HttpResponse::Ok().body(format!("検索: {} (最大{}件)", query.q, limit)) } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new().service(search) }) .bind("127.0.0.1:8080")? .run() .await }
curl "http://127.0.0.1:8080/search?q=rust&limit=5" # 検索: rust (最大5件)
JSONを返す
serde を使って構造体をJSONとして返せます。
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder}; use serde::Serialize; #[derive(Serialize)] struct User { id: u32, name: String, email: String, } #[get("/users/{id}")] async fn get_user(path: web::Path<u32>) -> impl Responder { let id = path.into_inner(); let user = User { id, name: "田中太郎".to_string(), email: "tanaka@example.com".to_string(), }; HttpResponse::Ok().json(user) } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new().service(get_user) }) .bind("127.0.0.1:8080")? .run() .await }
curl http://127.0.0.1:8080/users/1
# {"id":1,"name":"田中太郎","email":"tanaka@example.com"}
JSONボディを受け取る
POSTリクエストのJSONボディを受け取るには web::Json を使います。
use actix_web::{post, web, App, HttpResponse, HttpServer, Responder}; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize)] struct CreateUserRequest { name: String, email: String, } #[post("/users")] async fn create_user(body: web::Json<CreateUserRequest>) -> impl Responder { println!("新規ユーザー: {} ({})", body.name, body.email); HttpResponse::Created().json(&*body) } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new().service(create_user) }) .bind("127.0.0.1:8080")? .run() .await }
curl -X POST http://127.0.0.1:8080/users \
-H "Content-Type: application/json" \
-d '{"name":"山田花子","email":"yamada@example.com"}'
# {"name":"山田花子","email":"yamada@example.com"}
まとめ
| 機能 | 書き方 |
|---|---|
| GETルート | #[get("/path")] async fn handler() -> impl Responder |
| パスパラメータ | path: web::Path<T> |
| クエリパラメータ | query: web::Query<T>(Deserializeが必要) |
| JSONレスポンス | HttpResponse::Ok().json(value) |
| JSONボディ受信 | body: web::Json<T>(Deserializeが必要) |
| サーバー起動 | HttpServer::new(|| App::new().service(...)).bind(...).run() |
actix-webはRustのトレイトやライフタイムの知識があると理解しやすくなります。トレイトの基本は「Rustのトレイト入門:実装方法を基礎から解説」を参照してください。