芽萌丸プログラミング部@programming
投稿日 2025/3/5
更新日 2025/3/17 ✏

Rust: actixでwebアプリケーションのhelloworld

RustのWebフレームワークactix-webを使ってWebアプリケーションをhelloworldしてみるメモ。

環境

  • rustc 1.84.0
  • cargo 1.84.0
  • Ubuntu 22.04.5 LTS

プロジェクト新規作成

helloworldプロジェクトを新規作成:

$ cargo new helloworld
$ cd helloworld

Appサーバのコーディング

Cargo.toml

[package]
name = "helloworld"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-service = "2.0"
actix-web = "4"
env_logger = "0.11"
log = "0.4"

src/main.rs (Appサーバ)

use actix_web::{
    App, HttpRequest, HttpResponse, HttpServer, Responder, Result, get, middleware, web,
};
use log::error;
use std::panic;

/// GET / なRoute ("helloworld"を返すだけ)
#[get("/")]
async fn index(_req: HttpRequest) -> impl Responder {
    let result: Result<&str> = Ok("helloworld!");
    match result {
        // 200ステータスと"helloworld"を返す:
        Ok(msg) => HttpResponse::Ok().body(msg),
        // BadRequestなステータスを返す場合は以下のように書く: (このコードでは実際にはここは通らないが)
        Err(err) => HttpResponse::BadRequest().body(err.to_string()),
    }
}

/// GET /oops なRoute (内部でわざとpanicを起こす)
#[get("/oops")]
async fn oops(_req: HttpRequest) -> impl Responder {
    panic!("oops!");
    // panicが原因でここには到達しない!
    #[allow(unreachable_code)]
    HttpResponse::Ok().body("never here!")
}

/// 404 NotFound ハンドラ
async fn not_found_handler() -> impl Responder {
    HttpResponse::NotFound().body("not found!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // ロガー初期化 (ログlevelは"error")
    env_logger::init_from_env(env_logger::Env::new().default_filter_or("error"));

    // panicフックにpanic情報ログ処理を記述
    // NOTE: この処理は別になくてもいいが、有った方がpanicの調査に便利!
    panic::set_hook(Box::new(|info| {
        // panic情報を取得
        let location = info.location().unwrap();
        let payload = info.payload();

        // expect("")のパラメータやエラーデータからメッセージ文字列を作成
        let mut txt: String = "".to_string();
        if let Some(data) = payload.downcast_ref::<&str>() {
            txt.push_str(data);
        } else if let Some(data) = payload.downcast_ref::<String>() {
            txt.push_str(data);
        }

        // panic情報を表示
        let msg = format!(
            "panicked at {}:{}:{}: {}",
            location.file(),
            location.line(),
            location.column(),
            txt
        );
        error!("{}", msg);
    }));

    let server = HttpServer::new(move || {
        App::new()
            .wrap(middleware::Logger::default())
            .wrap(middleware::NormalizePath::new(
                middleware::TrailingSlash::MergeOnly,
            ))
            // Routesを設定:
            .service(index) // GET / なRouteをセット
            .service(oops) // GET /oops なRouteをセット
            // Routeが見つからなかった場合はここに来る:
            .default_service(web::to(not_found_handler))
    });
    let server = server.bind(("127.0.0.1", 8080))?;
    for addr in server.addrs() {
        println!("Listening on: http://{addr}/");
    }
    server.run().await
}

Appサーバの実行

Appサーバをビルド&実行:

## これだけでデバッグビルド後に実行される:
$ cargo run
Listening on: http://127.0.0.1:8080/

## or リリースビルドで実行したい場合:
$ cargo run --release
Listening on: http://127.0.0.1:8080/

curlでAppサーバのエンドポイントを叩いてみる:

## helloworldを返すだけのエンドポイントをコール:
$ curl 127.0.0.1:8080/
helloworld

## panicを起こすエンドポイントをコール:
$ curl 127.0.0.1:8080/oops
curl: (52) Empty reply from server

curlで/oopsエンドポイントを叩いたことでAppサーバのログに以下のようなpanic情報が出現:

[2025-03-04T02:34:51Z ERROR helloworld] panic "oops!" occurred at src/main.rs: 23

TIPs

ライブリロード

次の手順でコードをライブリロードすることができます。

  1. cargo-watchモジュールをインストール: (初回のみ)
    $ cargo install cargo-watch
    
  2. src配下のファイルの変更を監視し、変更が検知されたらcargo run実行:
    $ cargo watch -x run -w ./src
    

END


芽萌丸プログラミング部
芽萌丸プログラミング部@programming
プログラミング関連アカウント。Web標準技術を中心に書いていきます。フロントエンドからサーバサイドまで JavaScript だけで済ませたい人たちの集いです。記事は主に @TanakaSoftwareLab が担当。