2020年8月24日月曜日

Rustを学ぶ(2) : RustでAPIサーバー (GETとPOST)

はじめに

前回、Rustの環境構築をしたので今回はサーバーを実装します。

単純なAPIサーバーとして以下の実装をしていきます。

  1. GET /system/pingでpongを返す
  2. POST /system/ping{"ping" : "value"}を送ると、{"pong" : "value"}を返す

WebサーバーはTideを使います。Warpのような立ち位置にあり、簡単なWebサーバーを作ることができます。ちなみに、Tideはv0.13.0からProductionで使えるようになったようです。

Tideの起動


Tideを動かしてみます。

任意の場所でプロジェクトを作ります。

% cargo new example-tide
% cd example-tide

Cargo.tomlのdependenciesに以下を追記します。

[dependencies]
tide = "0.13.0"
async-std = { version = "1.6.0", features = ["attributes"] }

main.rsを以下に変更します。

// app.listen()がエラーを返す可能性があるのでResult型を受け付けるようにする
#[async_std::main]
async fn main() -> Result<(), std::io::Error> {
// ログ記録開始
tide::log::start();
// インスタンス生成
let mut app = tide::new();
// ルーティングとHTTPメソッドを設定し、何をするか決める
app.at("/").get(|_| async { Ok("Hello, world!") });
// webサーバーがどのIPアドレス、ポートで受け付けるか設定する
app.listen("127.0.0.1:8080").await?;
// 何もなければOkでタプルを返す
Ok(())
}
view raw main.rs hosted with ❤ by GitHub

ここまでできたらcargo runします。
試しにcurlしてみましょう。

% curl localhost:8080
Hello, world!

Webサーバーの起動に成功しました。

「GET /system/pingでpongを返す」を実装


以下のように5行目を変更します。

#[async_std::main]
async fn main() -> Result<(), std::io::Error> {
tide::log::start();
let mut app = tide::new();
app.at("/system/ping").get(|_| async { Ok("pong") });
app.listen("127.0.0.1:8080").await?;
Ok(())
}
view raw main.rs hosted with ❤ by GitHub

curlしてみましょう。

% curl localhost:8080/system/ping
pong

できましたが、APIサーバーとしては扱いやすいようにjsonで返したいです。

Rustでjsonといえばserde_jsonですが、Tideはすでにjson macroを持っています。
ですので、serde_jsonを使わずともjsonを返すことが可能です。
以下のように5行目を変更します。

use tide::prelude::*;
#[async_std::main]
async fn main() -> Result<(), std::io::Error> {
tide::log::start();
let mut app = tide::new();
app.at("/system/ping").get(|_| async { Ok(json!({"ok" : "pong"})) });
app.listen("127.0.0.1:8080").await?;
Ok(())
}
view raw main.rs hosted with ❤ by GitHub

curlしてみましょう。

% curl localhost:8080/system/ping
{"ok":"pong"}

jsonで返すことができました。

「POST /system/ping{"ping" : "value"}を送ると、{"pong" : "value"}を返す」を実装する


6行目にPOSTを受け付ける記述を追記します。(この時点ではPOSTパラメータを無視しています)

use tide::prelude::*;
#[async_std::main]
async fn main() -> Result<(), std::io::Error> {
tide::log::start();
let mut app = tide::new();
app.at("/system/ping").get(|_| async { Ok(json!({"ok" : "pong"})) });
app.at("/system/ping").post(|_| async { Ok(json!({"ok" : "pong"})) });
app.listen("127.0.0.1:8080").await?;
Ok(())
}
view raw main.rs hosted with ❤ by GitHub

curlしてみましょう。

% curl -XPOST localhost:8080/system/ping -d '{"ping":"value"}'
{"ok":"pong"}

やった!POSTで受付に成功しています。

それではPOSTパラメータを受け取り、値を返すようにします。
まずはserdeとserde_jsonをdependenciesに追加します。

[dependencies]
tide = "0.13.0"
async-std = { version = "1.6.0", features = ["attributes"] }
serde = "1.0.114"
serde_json = "1.0"

serdeは構造体をSerialize、Deserializeする時に使います。これで構造体を簡単にjsonにすることができます。(ここはもしかしたらTideがやってくれる...?)

以下のようにmainを修正します。

use tide::prelude::*;
use serde::{Deserialize, Serialize};
use tide::Request;
#[async_std::main]
async fn main() -> Result<(), std::io::Error> {
tide::log::start();
let mut app = tide::new();
app.at("/system/ping").get(|_| async { Ok(json!({"ok" : "pong"})) });
// requestを受け取って関数({}の中)に渡します
app.at("/system/ping").post(|mut request: Request<()>| async move {
// requestをjson形式にし、Ping型にします
let body: Ping = requet.body_json().await?;
// Ping型からpingの値だけを使い、Pong型にします
let res: Pong = Pong { pong: body.ping };
// Pong型のresをjsonで返します
Ok(json!(&res))
});
app.listen("127.0.0.1:8080").await?;
Ok(())
}
// 受取用の構造体
#[derive(Debug, Deserialize, Serialize)]
struct Ping { ping: String }
// 返却用の構造体
#[derive(Debug, Deserialize, Serialize)]
struct Pong { pong: String }
view raw main.rs hosted with ❤ by GitHub

curlしてみましょう。

% curl -XPOST localhost:8080/system/ping -d '{"ping":"value"}'
{"pong":"value"}

できました!

おわりに


今回はTideでルーティング、GETとPOSTができるようになりました。

次回は、ちゃんとResponseを返す(ステータスコードなど)ところまでやりたいと思います。

0 件のコメント:

コメントを投稿