Actix Web に入門して REST API サーバーを作る
はじめに
今回は Rust の Web フレームワークとしてかなり有名である、Actix-Web
のチュートリアル部分をやっていこうと思います。
Rust には Web フレームワークが多く存在しますが、その中でも比較的歴史があり、第一線で活躍するフレームワークの一つが Actix-Web
です。
チュートリアル的な内容ですので、これから Actix-Web
を触る方の助けになればと思います。
他のWebフレームワークとの比較は以下の記事が参考になりました。
🔻Actix Web
プロジェクト作成
事前に Rust
が入っていることが前提で進めていきます。
以下のコマンドで、 hello-world
プロジェクトを作成する。
cargo new hello-world
cd hello-world
以下のコマンドを実行し、actix-web
のクレートを追加します。
cargo add actix-web
Cargo.toml
が以下のように更新されていることを確認します。
[package]
name = "hello-world"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-web = "4.8.0"
※2024/07/15時点で最新バージョンが 4.8.0
でした。一旦現時点での最新バージョンでやっていきます。
念のため、私は cargo run
を実行しておきました。
cargo run
Hello, world!
REST API サーバーを構築する
メインの実装
main.rs
を以下のように編集します。
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
HttpResponse::Ok().body(req_body)
}
async fn manual_hello() -> impl Responder {
HttpResponse::Ok().body("Hey there!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(hello)
.service(echo)
.route("/hey", web::get().to(manual_hello))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
上記のコードでは、3つのエンドポイントが実装されているはずです。
- GET
/
- POST
/echo
- GET
/hey
それぞれのエンドポイントにアクセスして、どのような処理が行われているのかをざっくりとみていければと思います。
これで、 cargo run
を実行します。
動作の確認
/
GET 任意のブラウザで、http://127.0.0.1:8080/
にアクセスすると、Hello world!
が出力されていることを確認できました。
main
関数の中で以下の部分が呼び出されて、Hello world!
が出力される仕組みですね。
関数の前に、メソッドとパスがそのまま記述されていることがわかります。
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
/hey
GET こちらも、任意のブラウザでhttp://127.0.0.1:8080/hey
にアクセスすると、Hey there!
と出力されることを確認することができました。
これは、main
関数の中にメソッドとパスが記述されているタイプで、呼び出し先の関数のみを定義する方法のようです。
ルーティングの方法に2つの実装方法があるようですね!
async fn manual_hello() -> impl Responder {
HttpResponse::Ok().body("Hey there!")
}
/echo
POST POST エンドポイントですので、任意のクライアント実行ツールを使い、
http://127.0.0.1:8080/echo
にアクセスします。(私は Thunder Clientという VScode の拡張機能を使用します。)
Body
には任意の JSON
を入れることで、そのまま応答として返されます。
こちらも、Hello, world!
出力のパターンと同様に、echo
関数の前にメソッドやパスを定義していることがわかります。
#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
HttpResponse::Ok().body(req_body)
}
追加の実装をしてみる
チュートリアルのコピペでは楽しくないので、
簡単ですが、ヘルスチェックのエンドポイントを実装していきます!
GET /health
にアクセスすると以下のような JSON
が帰ってくることとして実装します。
{
"error": false, // エラーかどうか
"date_time": "2024-07-15 18:57:13" // 現在の日時
}
以下のコマンドで、serde
と chrono
を追加します。
cargo add serde --features derive
cargo add chrono
main.rs
に以下のコードを追加してみます。
use chrono::Local;
use serde::Serialize;
#[derive(Serialize)]
struct HealthResponse {
error: bool,
date_time: String,
}
#[get("/")]
async fn health() -> impl Responder {
let response = HealthResponse {
error: false,
date_time: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
};
web::Json(response)
}
さらに、ルーティングの部分に以下を追加。
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(hello)
.service(echo)
// 新しく追加
.service(health)
.route("/hey", web::get().to(manual_hello))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
ここまでできたら、再度 cargo run
を実行し確認してみます。
ブラウザか、APIクライアントツールで http://127.0.0.1:8080/health
にアクセスします。
以下のように、応答が返ってくることが確認できました!
おわりに
いかがでしたでしょうか。
チュートリアルと、追加のエンドポイントの実装を行ってみました。
贔屓目なしでもとても簡単に REST API サーバーが構築できると感じました。
ルーティング部分が非常に簡易的だと感じました。
.route()
、.service()
のどちらでルーティングする方が多いのでしょうね。
ここはプロジェクト文化によって変わるところかもしれませんが、私は後者の方が良いかなと思いました。
また Actix-Web
のあれこれを記事化できればと思います!
では。
Discussion