📟

RustでWebサイトを作る[by Chat-GPT4]

2023/03/17に公開約7,500字

Rustはシステムプログラミング言語として知られていますが、Web開発にも優れていることがあります。この記事では、Rustを使ってWebサイトを作成する方法を紹介します。

概要

  1. 開発環境のセットアップ
  2. Webフレームワークの選択
  3. アプリケーションの構築
  4. テンプレートエンジンの選択
  5. 静的ファイルの配信
  6. データベースの接続

1. 開発環境のセットアップ

まず、Rustの開発環境をセットアップしましょう。公式サイトからRustのインストーラをダウンロードし、インストールを行います。

2. Webフレームワークの選択

RustにはいくつかのWebフレームワークが存在します。主なものには以下があります。

  • Actix Web
  • Rocket
  • Tide
  • Warp

今回は高いパフォーマンスと柔軟性が魅力のActix Webを使って解説します。

3. アプリケーションの構築

下記のコマンドでRustプロジェクトを作成します。

$ cargo new my-web-app

Cargo.tomlにActix Webの依存関係を追加します。

[dependencies]
actix-web = "4"
actix-rt = "2.2"

次に、src/main.rsを作成し、基本的なサーバー構成を設定します。

use actix_web::{web, App, HttpServer, Responder};

async fn index() -> impl Responder {
    "Hello, World!"
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(index))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

これで、cargo runコマンドを実行すると、http://127.0.0.1:8080でWebサイトが立ち上がります。

4. テンプレートエンジンの選択

Rustにはいくつかのテンプレートエンジンがあります。代表的なものとしては以下が挙げられます。

  • Handlebars
  • Tera
  • Askama
    この例では、Teraを使って解説します。まず、Cargo.tomlに依存関係を追加します。
[dependencies]
tera = "1.0"

5. テンプレートの作成とレンダリング

src/templatesディレクトリを作成し、index.htmlテンプレートファイルを作ります。

<!-- src/templates/index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Rust Webサイト</title>
</head>
<body>
    <h1>Hello, {{ name }}!</h1>
</body>
</html>

次に、src/main.rsでTeraを使ってテンプレートをレンダリングし、レスポンスとして返します。

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use tera::{Tera, Context};

async fn index(tera: web::Data<Tera>) -> impl Responder {
let mut context = Context::new();
context.insert("name", "Rustacean");
let rendered = tera.render("index.html", &context).unwrap();
HttpResponse::Ok().body(rendered)
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
let tera = Tera::new("src/templates/**/*").unwrap();

HttpServer::new(move || {
    App::new()
        .data(tera.clone())
        .route("/", web::get().to(index))
})
.bind("127.0.0.1:8080")?
.run()
.await
}

これで、テンプレートがレンダリングされ、http://127.0.0.1:8080にアクセスすると"Hello, Rustacean!"と表示されます。

6. 静的ファイルの配信

静的ファイル(CSS, JS, 画像など)を配信するために、Actix Webのactix_filesを使います。まず、Cargo.tomlに依存関係を追加します。

[dependencies]
actix-files = "0.6"

次に、src/main.rsでactix_files::Filesを使って静的ファイルを配信します。ここでは、srcと並列にstaticディレクトリを作成し、その中に静的ファイル(test.jpg)を配置します。

use actix_files as fs;
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use tera::{Context, Tera};

async fn index(tera: web::Data<Tera>) -> impl Responder {
    let mut context = Context::new();
    context.insert("name", "Rustacean");

    let rendered = tera.render("index.html", &context).unwrap();
    HttpResponse::Ok().body(rendered)
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    let tera = Tera::new("src/templates/**/*").unwrap();

    HttpServer::new(move || {
        App::new()
            .data(tera.clone())
            .route("/", web::get().to(index))
            .service(fs::Files::new("/static", "static/"))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

これで、staticディレクトリ内のファイルがhttp://127.0.0.1:8080/static/test.jpgパスでアクセスできるようになります。

7. データベースの統合

データベースを利用するために、Rustの非同期ORMであるSQLxを使います。Cargo.tomlに依存関係を追加します。
また、データをシリアライズするために、serdeとserde_jsonクレートを使用します。

[dependencies]
sqlx = { version = "0.5", features = ["postgres", "runtime-actix-rustls"] }
dotenv = "0.15"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

.envファイルを作成し、データベース接続情報を記述します。

DATABASE_URL=postgres://username:password@localhost/dbname

次に、src/main.rsでデータベース接続を行います。

use actix_files as fs;
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use dotenv::dotenv;
use serde::Serialize;
use sqlx::{FromRow, PgPool};
use std::env;
use tera::{Context, Tera};

// src/main.rs

#[derive(Debug, FromRow, Clone, Serialize)]
struct User {
    id: i32,
    name: String,
    email: String,
}

async fn get_users(pool: &PgPool) -> Result<Vec<User>, sqlx::Error> {
    let users = sqlx::query_as!(User, "SELECT id, name, email FROM users")
        .fetch_all(pool)
        .await?;
    Ok(users)
}

async fn index(tera: web::Data<Tera>, pool: web::Data<PgPool>) -> impl Responder {
    let mut context = Context::new();
    context.insert("name", "Rustacean");
    let users = get_users(&pool).await.unwrap();
    let mut context = Context::new();
    context.insert("users", &users);

    let rendered = tera.render("index.html", &context).unwrap();
    HttpResponse::Ok().body(rendered)
}

#[actix_rt::main]

async fn main() -> std::io::Result<()> {
    dotenv().ok();
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");

    let pool = PgPool::connect(&database_url)
        .await
        .expect("Failed to create database connection pool");

    let tera = Tera::new("src/templates/**/*").unwrap();

    HttpServer::new(move || {
        App::new()
            .data(pool.clone())
            .data(tera.clone())
            .route("/", web::get().to(index))
            .service(fs::Files::new("/static", "static/"))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

これで、データベースからユーザーの一覧を取得し、テンプレートに渡すことができます。テンプレート内でユーザーの一覧を表示するには、以下のようにループを使って表示します。

<!-- src/templates/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Rust Webサイト</title>
</head>
<body>
    <h1>ユーザー一覧</h1>
    <table>
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>Email</th>
        </tr>
        {% for user in users %}
        <tr>
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
            <td>{{ user.email }}</td>
        </tr>
        {% endfor %}
    </table>
</body>
</html>

以下に、サンプルデータをusersテーブルに挿入するSQL文を示します。

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL
);
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
INSERT INTO users (name, email) VALUES ('Charlie', 'charlie@example.com');
INSERT INTO users (name, email) VALUES ('David', 'david@example.com');

これらのSQL文をデータベース管理ツールやコマンドラインツール(例:psql)を使って実行することで、usersテーブルにサンプルデータを挿入できます。挿入されたデータは、上記で実装したRust Webアプリケーションで表示されます。

Rustを使ってWebサイトを作成するためには、適切なフレームワーク、テンプレートエンジン、データベース接続、静的ファイルの配信を行う必要があります。この記事で紹介した方法を参考に、Rustで高速で安全なWebサイトを構築してみてください。

出力結果

人間からのコメント

プロンプトも全く工夫せずに「RustでWebサイトを作る技術記事」とだけ打って錬成されたものなので、9割9分Chat GPT-4氏の制作になります。1ミリも独自性はありません。恐らく地球上の既存の記事から錬成された物でしょうから、まずは先人に感謝です。

残りの1%の修正は、cargo newの部分と、データベース接続は接続しただけで満足していたので、追記をお願いしました。
今度は

  • もっと話口調を面白くする
  • テーマの濃さを求める
  • 実際に自分の要求を貫いた制作物を作り上げる
  • 下書きとして、自分の創作性を込めた文章を書くときのコストダウンとして使う

あたりにチャレンジして、Chat-GPTとの距離感を測っていきたいです。
まぁ、余計なことを言うと、プログラミングをするのは楽しいのですが、「Rustでウェブアプリ入門」までだと、ライブラリが多くをカバーしてしまうので、Rust感を味わう前に終わっていてつまらないなと以前から感じていました。作ろうと思っても作る気が気づいたらなくなっている。言い訳かな?データベース接続・表示の流れ自体は単純でプログラミングというより作業だと感じて嫌になっていた節もあるので、そこをサクッとやってくれるのは助かります。最初は癖でSQLを手書きしようとしていたのですが、頼んだら一瞬で作ってくれたので、もうそういう作業に頭動かすのはやめて、別のことに使いたいと思います。

Discussion

ログインするとコメントできます