Skip to content

演習問題

基本問題は解答を置いてありますが、できるだけ自力で頑張ってみてください。

最後に全問題をまとめて動作確認できるシェルスクリプトが置いてあるので、作ったエンドポイントは消さないで残しておくと良いです。

基本問題 GET /ping

pong と返すエンドポイントを作成してください。

スキーマ

リクエスト

/ping

レスポンス

content-type: text/plain status code: 200

pong

完成したら、以下のコマンドをターミナルで実行して上手く機能しているか確認しましょう。

bash
$ curl -X GET "http://localhost:8080/ping" # pong
解答
rs
use axum::{routing::get, Router};

#[tokio::main]
async fn main() {
    // 「/ping」というエンドポイントを設定する
    let app = Router::new().route("/ping", get(handler));

    // ポート8080でリスナーを作成する
    let listener = tokio::net::TcpListener::bind("127.0.0.1:8080")
        .await
        .unwrap();

    println!("listening on {}", listener.local_addr().unwrap());

    // サーバーを起動する
    axum::serve(listener, app).await.unwrap();
}

// 文字列「pong」をクライアントに返す
async fn handler() -> String {
    String::from("pong")
}

基本問題 GET /fizzbuzz

クエリパラメータcountで渡された数までの FizzBuzz を返してください。
countがない場合は 30 として扱い、countが整数として解釈できない場合はステータスコード400を返してください。

スキーマ

リクエスト

/fizzbuzz?count=10

レスポンス

content-type: text/plain status code: 200

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
適切でないリクエストの場合

content-type: text/plain status code: 400

Bad Request

完成したら、以下のコマンドをターミナルで実行して上手く機能しているか確認しましょう。

bash
$ curl -X GET "http://localhost:8080/fizzbuzz?count=20"
$ curl -X GET "http://localhost:8080/fizzbuzz" # count=30 と同じ
$ curl -X GET "http://localhost:8080/fizzbuzz?count=a" # Bad Request

/fizzbuzzが上手く動いたら、講習会の実況用チャンネルに↑の実行結果を投稿しましょう!

解答
rs
use axum::{extract::Query, http::StatusCode, routing::get, Router};
#[tokio::main]
async fn main() {
    // 「/ping」というエンドポイントを設定する
    let app = Router::new().route("/fizzbuzz", get(fizzbuzz_handler));

    // ポート8080でリスナーを作成する
    let listener = tokio::net::TcpListener::bind("127.0.0.1:8080")
        .await
        .unwrap();

    println!("listening on {}", listener.local_addr().unwrap());

    // サーバーを起動する
    axum::serve(listener, app).await.unwrap();
}

// クエリパラメータを受け取るための構造体を定義
#[derive(serde::Deserialize)]
struct FizzBuzzQuery {
    count: Option<String>,
}

async fn fizzbuzz_handler(Query(query): Query<FizzBuzzQuery>) -> (StatusCode, String) {
    // クエリパラメータが指定されていない場合はデフォルト値を使用する
    let mut n: i32 = 30;
    // クエリパラメータが指定されている場合はその値を調べる
    if let Some(count) = query.count {
        let count = count.parse();
        match count {
            // 数値に変換できた場合はその値を使用する
            Ok(count) => n = count,
            // ステータスコード 400 Bad Request を返す
            Err(_) => return (StatusCode::BAD_REQUEST, String::from("Bad Request\n")),
        }
    }

    // FizzBuzzの処理をする
    let fizzbuzz_str = fizzbuzz(n);

    // ステータスコード 200 Ok とfizzBuzzの結果を返す
    (StatusCode::OK, fizzbuzz_str + "\n")
}

// fizzBuzzの処理
fn fizzbuzz(n: i32) -> String {
    let mut result = String::new();
    for i in 1..=n {
        if i % 15 == 0 {
            result.push_str("FizzBuzz\n");
        } else if i % 3 == 0 {
            result.push_str("Fizz\n");
        } else if i % 5 == 0 {
            result.push_str("Buzz\n");
        } else {
            result.push_str(&i.to_string());
            result.push('\n');
        }
    }
    result
}

基本問題 POST /add

送信される値を足した数値を返してください。

スキーマ

リクエスト

content-type: application/json

json
{
    "left": 27,
    "right": 57
}

レスポンス

content-type: application/json status code: 200

json
{
    "answer": 84
}
適切でないリクエストの場合

content-type: application/json status code: 400

json
{
    "error": "Bad Request"
}

を返してください。

完成したら、以下のコマンドをターミナルで実行して上手く機能しているか確認しましょう。

bash
$ curl -X POST "http://localhost:8080/add" -H "Content-Type: application/json" -d '{"left": 18781, "right": 18783}' # 37564
$ curl -X POST "http://localhost:8080/add" -H "Content-Type: application/json" -d '{"left": 0, "right": -0}' # 0
$ curl -X POST "http://localhost:8080/add" -H "Content-Type: application/json" -d '{"left": a, "right": b}' # Bad Request
$ curl -X POST "http://localhost:8080/add" -H "Content-Type: application/json" -d '{"left": 100}' # Bad Request
解答
rs
use axum::extract::rejection::JsonRejection;
use axum::{http::StatusCode, routing::post, Json, Router};

#[tokio::main]
async fn main() {
    // 「/ping」というエンドポイントを設定する
    let app = Router::new().route("/add", post(add_handler));

    // ポート8080でリスナーを作成する
    let listener = tokio::net::TcpListener::bind("127.0.0.1:8080")
        .await
        .unwrap();

    println!("listening on {}", listener.local_addr().unwrap());

    // サーバーを起動する
    axum::serve(listener, app).await.unwrap();
}

// クエリパラメータを受け取るための構造体を定義
#[derive(serde::Deserialize)]
struct AddQuery {
    left: f64,
    right: f64,
}

// レスポンスとして返す構造体を定義
#[derive(serde::Serialize)]
struct AddResponse {
    result: i64,
}

// エラーレスポンスとして返す構造体を定義
#[derive(serde::Serialize)]
struct AddError {
    error: String,
}

async fn add_handler(
    query: Result<Json<AddQuery>, JsonRejection>,
) -> Result<Json<AddResponse>, (StatusCode, Json<AddError>)> {
    match query {
        // クエリが正しく受け取れた場合、クライアントに結果を返す
        Ok(query) => Ok(Json(AddResponse {
            result: (query.left + query.right) as i64,
        })),
        // クエリが正しく受け取れなかった場合、エラーを返す
        Err(_) => Err((
            StatusCode::BAD_REQUEST,
            Json(AddError {
                error: String::from("Bad Request"),
            }),
        )),
    }
}

発展問題 GET /students/:class/:studentNumber

前提:以下のデータをサーバー側で持っておく。

json
[
  {"class_number": 1, "students": [
    {"student_number": 1, "name": "pikachu"},
    {"student_number": 2, "name": "ikura-hamu"},
    {"student_number": 3, "name": "noc7t"}
  ]},
  {"class_number": 2, "students": [
    {"student_number": 1, "name": "Sora"},
    {"student_number": 2, "name": "Kaito"},
    {"student_number": 3, "name": "Haruka"},
    {"student_number": 4, "name": "Shingo"}
  ]},
  {"class_number": 3, "students": [
    {"student_number": 1, "name": "Hikaru"},
    {"student_number": 2, "name": "Eri"},
    {"student_number": 3, "name": "Ryo"}
  ]},
  {"class_number": 4, "students": [
    {"student_number": 1, "name": "Marina"},
    {"student_number": 2, "name": "Takumi"}
  ]}
]

classNumberstudentNumber に対応する学生の情報を JSON で返してください。
学生が存在しない場合、404を返してください。

スキーマ

リクエスト

/students/2/3

レスポンス

content-type: application/json status code: 200

json
{
    "student_number": 3,
    "name": "Haruka"
}
学生が存在しない場合

content-type: application/json status code: 404

json
{
    "error": "Student Not Found"
}

TIP

ヒント: 最後の課題のデータは次のような構造体を用意して、serde_json::from_str すると定義しやすいです。

rs
#[derive(serde::Serialize, serde::Deserialize, clone::Clone)]
struct Student {
    student_number: u32,
    name: String,
}

#[derive(serde::Deserialize)]
struct Class {
    class_number: u32,
    students: Vec<Student>,
}

完成したら、以下のコマンドをターミナルで実行して上手く機能しているか確認しましょう。

bash
$ curl -X GET "http://localhost:8080/students/1/1" # pikachu
$ curl -X GET "http://localhost:8080/students/3/4" # Student Not Found
$ curl -X GET "http://localhost:8080/students/5/1" # Student Not Found

自分のサーバーが正しく動作しているか確認しよう

カレントディレクトリ(main.goがあるディレクトリ)に、新しくtest.shというファイルを作成し、以下をコピー&ペーストしてください。

bash
#!/bin/bash

# ↓これを自分のIDに変更してください
ID=kenken
# ↑これを自分のIDに変更してください

echo ""
echo "===================="
echo "[TEST] /${ID}"
echo 'curl -X GET http://localhost:8080/'${ID}
curl -X GET "http://localhost:8080/${ID}"
echo ""
echo "===================="
echo "[TEST] /ping"
echo 'curl -X GET http://localhost:8080/ping'
curl -X GET "http://localhost:8080/ping"
echo ""
echo "===================="
echo "[TEST] /fizzbuzz 1of3"
echo '-X GET http://localhost:8080/fizzbuzz?count=20'
curl -X GET "http://localhost:8080/fizzbuzz?count=20"
echo ""
echo "===================="
echo "[TEST] /fizzbuzz 2of3"
echo 'curl -X GET http://localhost:8080/fizzbuzz'
curl -X GET "http://localhost:8080/fizzbuzz"
echo ""
echo "===================="
echo "[TEST] /fizzbuzz 3of3"
echo 'curl -X GET http://localhost:8080/fizzbuzz?count=a'
curl -X GET "http://localhost:8080/fizzbuzz?count=a"
echo ""
echo "===================="
echo "[TEST] /add 1of4"
echo 'curl -X POST http://localhost:8080/add -H "Content-Type: application/json" -d "{\"left\": 18781, \"right\": 18783}"'
curl -X POST "http://localhost:8080/add" -H "Content-Type: application/json" -d '{"left": 18781, "right": 18783}'
echo ""
echo "===================="
echo "[TEST] /add 2of4"
echo 'curl -X POST http://localhost:8080/add -H "Content-Type: application/json" -d "{\"left\": 0, \"right\": -0}"'
curl -X POST "http://localhost:8080/add" -H "Content-Type: application/json" -d '{"left": 0, "right": -0}'
echo ""
echo "===================="
echo "[TEST] /add 3of4"
echo 'curl -X POST http://localhost:8080/add -H "Content-Type: application/json" -d "{\"left\": a, \"right\": b}"'
curl -X POST "http://localhost:8080/add" -H "Content-Type: application/json" -d '{"left": a, "right": b}'
echo ""
echo "===================="
echo "[TEST] /add 4of4"
echo 'curl -X POST http://localhost:8080/add -H "Content-Type: application/json" -d "{\"left\": 100}"'
curl -X POST "http://localhost:8080/add" -H "Content-Type: application/json" -d '{"left": 100}'
echo ""
echo "===================="
echo "[TEST] /students 1of3"
echo 'curl -X GET http://localhost:8080/students/1/1'
curl -X GET "http://localhost:8080/students/1/1"
echo ""
echo "===================="
echo "[TEST] /students 2of3"
echo 'curl -X GET http://localhost:8080/students/3/4'
curl -X GET "http://localhost:8080/students/3/4"
echo ""
echo "===================="
echo "[TEST] /students 3of3"
echo 'curl -X GET http://localhost:8080/students/5/1'
curl -X GET "http://localhost:8080/students/5/1"
echo ""

ペーストした後、ファイル内の以下の部分を自分の ID に書き換えてください。

# ↓これを自分のIDに変更してください
ID=kenken
# ↑これを自分のIDに変更してください

最後に、ターミナルを開き、以下を実行してください。

bash
$ chmod +x test.sh # 実行権限を付与
$ ./test.sh # シェルスクリプトtest.shを実行

使用例は以下の通りです。

使用例
$ ./test.sh 

====================
[TEST] /kenken
curl -X GET http://localhost:8080/kenken
始めまして、@kenkenです。
きらら作品(特に恋する小惑星、スロウスタート)が好きです。

====================
[TEST] /ping
curl -X GET http://localhost:8080/ping
pong
====================
[TEST] /fizzbuzz 1of3
-X GET http://localhost:8080/fizzbuzz?count=20
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz

====================
[TEST] /fizzbuzz 2of3
curl -X GET http://localhost:8080/fizzbuzz
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz

====================
[TEST] /fizzbuzz 3of3
curl -X GET http://localhost:8080/fizzbuzz?count=a
Bad Request

====================
[TEST] /add 1of4
curl -X POST http://localhost:8080/add -H "Content-Type: application/json" -d "{\"left\": 18781, \"right\": 18783}"
{"result":37564}
====================
[TEST] /add 2of4
curl -X POST http://localhost:8080/add -H "Content-Type: application/json" -d "{\"left\": 0, \"right\": -0}"
{"result":0}
====================
[TEST] /add 3of4
curl -X POST http://localhost:8080/add -H "Content-Type: application/json" -d "{\"left\": a, \"right\": b}"
{"error":"Bad Request"}
====================
[TEST] /add 4of4
curl -X POST http://localhost:8080/add -H "Content-Type: application/json" -d "{\"left\": 100}"
{"error":"Bad Request"}
====================
[TEST] /students 1of3
curl -X GET http://localhost:8080/students/1/1
{"student_number":1,"name":"pikachu"}
====================
[TEST] /students 2of3
curl -X GET http://localhost:8080/students/3/4
{"error":"Student not found"}
====================
[TEST] /students 3of3
curl -X GET http://localhost:8080/students/5/1
{"error":"Student not found"}

ここまで出来たら、講習会の実況用チャンネルに./test.shの出力結果を貼りましょう!
発展問題は出来てなくても大丈夫ですが、チャレンジしてみてください。

TIP

出力結果は長いので、
```
ここに内容
```
のように内容を```で囲い、コードブロックにして送信すると良いです。