2020年8月31日月曜日

Rustを学ぶ(2.5) : RustでAPIサーバー (Responseを返す)

はじめに

前回は、GETとPOSTができるようになりました。

ただし、誤ったリクエストを送った時に、その誤りの理由までは教えてくれません。

また、任意のステータスコードを返せるようになったら、連携するアプリケーション側で考慮するステータスコードを絞れます。

今回は、以下の2つを実装します。

  1. GETとPOSTで/system/error にアクセスすると404が返り、「wrong resource access.」を返す
  2. POSTで/system/pingに'{"wrong":"error"}' でアクセスすると400が返り、「wrong request.」を返す


実装する前に

そもそも現在はどういうステータスコードが返るのでしょうか?

まずは1のGETをしてみます。

% curl localhost:8080/system/error -v
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8080 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /system/error HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< content-length: 0
< date: Sun, 30 Aug 2020 00:16:52 GMT
<
* Connection #0 to host localhost left intact
* Closing connection 0

404が返ります。1のPOSTもしてみます。

% curl -XPOST localhost:8080/system/error -v
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8080 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /system/error HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< content-length: 0
< date: Sun, 30 Aug 2020 14:21:27 GMT
<
* Connection #0 to host localhost left intact
* Closing connection 0

こちらも404です。理由は特に書かれていません。

2もやってみます。


% curl -XPOST localhost:8080/system/ping -d '{"wrong":"error"}'  -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8080 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /system/ping HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 17
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 17 out of 17 bytes
< HTTP/1.1 422 Unprocessable Entity
< content-length: 0
< date: Sun, 30 Aug 2020 14:24:20 GMT
<
* Connection #0 to host localhost left intact
* Closing connection

422 (Unprocessable Entity)が返っています。これは正しそうな挙動ですが、アプリケーション側で422を考慮するコードを書く必要が出てきます。今のところ私しか使う人もいないですし、ここは400 (Bad Request)でいいと考えています。もし必要ならコードを読めばいいのです。

余談ですが、curlに"Note: Unnecessary use of -X or --request, POST is already inferred."と出ています。初めて知りましたが、実はXPOSTを付けなくても-dのPOSTデータがあれば、curlはPOSTで送ることを推測してくれるみたいです。


「GETとPOSTで/system/error にアクセスすると404が返り、「wrong resource access.」を返す」を実装する


1を実装します。

9~18行目までにtide::utils::Afterを使って、外にResponseを出す前に返すレスポンス処理を加えます。

curlでGETしてみます。


% curl localhost:8080/system/error -v
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8080 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /system/error HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< content-length: 30
< date: Sun, 30 Aug 2020 15:25:11 GMT
< content-type: text/plain;charset=utf-8
<
Error: wrong resource access.
* Connection #0 to host localhost left intact
* Closing connection 0

POSTもしてみます。


 % curl localhost:8080/system/error -d '{"wrong":"error"}' -v
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8080 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /system/error HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 17
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 17 out of 17 bytes
< HTTP/1.1 404 Not Found
< content-length: 30
< date: Sun, 30 Aug 2020 15:26:14 GMT
< content-type: text/plain;charset=utf-8
<
Error: wrong resource access.
* Connection #0 to host localhost left intact
* Closing connection 0

できているようです。


「POSTで/system/pingに'{"wrong":"error"}' でアクセスすると400が返り、「wrong request.」を返す」を実装する


2を実装していきます。

先ほどと同じように実装を追加していきます。16~20行目に追記しました。

curlしてみます。


% curl localhost:8080/system/ping -d '{"wrong":"error"}' -v
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8080 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /system/ping HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 17
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 17 out of 17 bytes
< HTTP/1.1 400 Bad Request
< content-length: 22
< date: Sun, 30 Aug 2020 15:33:26 GMT
< content-type: text/plain;charset=utf-8
<
Error: wrong request.
* Connection #0 to host localhost left intact
* Closing connection 0

できてる!


おわりに

今回はResponseを任意のステータスコードで、任意のメッセージを返す実装をしました。

次回はDBへの疎通確認をしたいと思います。


参考文献

先輩と覚える HTTP ステータスコード


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を以下に変更します。


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

% curl localhost:8080
Hello, world!

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

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


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


curlしてみましょう。

% curl localhost:8080/system/ping
pong

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

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


curlしてみましょう。

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

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

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


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


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を修正します。


curlしてみましょう。

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

できました!

おわりに


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

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

2020年8月17日月曜日

Amazon Honeycodeでノーコードな家計簿アプリをつくった

要約

  • Amazon Honeycodeでコードを書かずに家計簿アプリをつくりました。
  • 部品をポチポチしながらテーブルからデータを読み出すだけでWeb+スマホアプリが作れます。
  • マニュアルが英語、保守性、保存できるデータ量に課題がありますが、これからの機能拡張と改善に期待したいです。
Honeycodeで作ったスマホ家計簿アプリ


はじめに - 家計簿の話


僕は今2人暮らしです。
お互いが必要な物を買うとGoogle スプレッドシートの家計簿に金額をつけています。
月末にワリカンするため、平均値と合計値を出して、少ない方が多い方に振り込んでいます。

僕たちはこれを2年間やってきたわけですが、デメリットが3つあります。
  1. スマホから入力しづらく、つけ忘れが発生する
  2. 月初めにシートを新しく作る必要がある
  3. 振り込み忘れが発生する

今まで使っていた家計簿

今月で使用3年目を迎えるため、これを機にアプリを作ってみることにしました。

Amazon Honeycodeを選んだ理由


アプリを作るにはいくつも方法があります。たとえばFlutterとか。
ただし、すぐに使えるように1日以内で作りたいです。
そこで、ちょうどはれんちさんにオススメされたHoneycodeで作ることにしました。

Amazon Honeycodeの概要については次の記事が詳しいです。

あとで知りましたが、honeycodeみたいなサービスはいくつもあるらしいです。

どう作った?


次の要領でやりました。
  1. やりたいこと&(理想的な)画面ラフ作成
  2. データ構造の決定
  3. (現実的な)画面作成
  4. 2と3を行ったり来たりして完成

1. やりたいこと&(理想的な)画面ラフ作成


まずは、やりたいことを考えていきます。
  • 家計簿に登録したい
  • 今月いくら使ったか見たい
  • 振り込みは済んでいるのかを見たい
などなど。この時に画面のラフがあるとイメージが湧きます。

2. データ構造の決定


次にデータ構造を考えます。

Honeycodeは、データ構造(表計算)と画面(View)の組み合わせで作ります。
データ構造は本番以降の変更が難しいです。しっかり考える必要があります。
ノーコードアプリの開発者は主にエンジニアではない人だと思います。
ですので、ここは本職の方またはデータ構造の専門家に見てもらった方が良いと思います。

今回は次のようにデータ構造を考えました。

家計簿アプリのデータ構造と画面



データ構造が確定したら実際にテーブルを作っていきます。この時、テストデータを入れておくと次の画面作成でイメージが膨らむので便利です。

テーブル例


3. 画面作成


honeycodeはデフォルトでアプリ例がいくつかあります。それらの例とやりたいことを比べて参考にしました。
最初に画面を作る時にはウィザードが立ち上がります。データを並べるだけならばウィザードだけで十分だと思います。

次の画面は僕たちが作成した画面です。ウィザードで出てくる要らない部品は消し、必要な部品を足しています。例えば「今月の出費」の直下にある合計と平均を表示する部分は自分で作っています。

画面作成

部品はたくさんありますが、ほとんどの場合は下の3つで足ります。

- Data & Display
    - Data cell ... データを表示する。関数を使って絞り込める。なぜか入力も受け付けられる。
    - Content box ... 表示を出す。この中にData Cellを入れることが多い。
    - Button ... ボタン。ボタンを押したらどうなるかはActionで定義する。

関数はExcelの関数とほぼ一緒です。Honeycodeには便利な関数が追加されているので1つだけ紹介します。僕はこれだけで十分でした。

テーブルのデータをフィルタしたい時に使う。リストされた行が返ってくる。

書き方
=FILTER(table, "condition", [condition_parameters, ...])

例 月初から月末までのデータをフィルタしたい

=FILTER(payment,"payment[date]>=% AND payment[date]<% ORDER BY payment[date] DESC",DATE(YEAR(TODAY()),MONTH(TODAY()),1),DATE(YEAR(TODAY()),MONTH(TODAY())+1,0))


%には3つ目以降のデータが入ります。
ご覧の通りSQLみたいな条件が書けます。これが非常に便利です。

他にも様々な関数がありますので、迷った場合は見てください。ただし英語です。

困ったとき


フォーラムがあります。英語です。悩んでいることは、ほとんどの場合は解決されているか、未実装です。


リリース


2020/08/15(土)時点ではプロジェクトメンバー以外がアプリに触ることはできないようです。ですので、触りたい人はプロジェクトメンバーになる必要があります。

Webアプリは開発画面からアクセスします。
スマホアプリでは専用のアプリをダウンロードします。

課題


作っていた時に感じたHoneycodeの課題感です。
  • 英語しかない
    • 今後改善されると思います。google翻訳に頼りましょう。
  • 通貨計算がドルしかない
    • ユーロもポンドもない。まだベータですので今後に期待です。
  • 簡単にグラフが描けない
  • 無料では1テーブルに2500行までしか入らない
    • 有料でも無限になることはありません。
    • もし大量にデータがあった場合は処理が遅くなるのか、は測定していないのでわかりません。
    • データの追加よりも更新の頻度が多いなら問題ないでしょう。
  • プロジェクトチームが20人まで
    • 無料でも有料でも同じです。
    • 企業で使われる想定だと思うので、チームメンバーでなくても触れるようにするか、チームメンバーを増やすかしていただきたいです。
  • 保守が難しくなる可能性が高そう
    • 場合によりますが、コードがないことも起因するのか保守が独特な感じがします。
    • ロジックを分散して書きすぎると、どこにロジックがあるか分からず、保守性が落ちると思います。
      • できる限り、テーブル側にロジックを持たせて、画面では簡単な表示にした方が良い印象です。
    • またテストを書き残せないので、例えば「テストテーブルを作って本当に動くか確かめてみよう」は本番稼働後には難しいと思います。
      • 事前にたくさんテストしましょう。

さいごに


Google スプレッドシートで管理していた時よりも楽に家計簿をつけられています。
スマホから簡単に操作できる点が素晴らしいです。
数年後にはリプレースしようとは考えていますが、現状ではベターな選択肢の1つだと思います。
なにより、実現することないソフトウェアよりも動くソフトウェアを作る観点でみると、ITリソースが足りていない状況では素晴らしい力になると思いました。

2020年8月2日日曜日

久しぶりにオートマトンを勉強した

ソフトウェア工学輪読会


という勉強会を社内でやってます。
ソフトウェア工学という本を持ち回りで読んでいくのですが、その6回目を僕が担当しました。
内容はオートマトンとソフトウェアです。

大学2年生で「オートマトンと言語処理」という講義を取って以来です。
そのときは音声と言語処理に傾いた授業で、のちに「コンパイラや正規表現でオートマトンが使われているらしい」程度の知識を得ました。

改めて勉強してみると興味深い分野です。
オートマトン(状態遷移図)を書き起こすとソフトウェアが何をするのかが具体的になります。
さらに興味深いことに、数学的に考えることが可能であるということです。様々な拡張や表現を増やしていける余地があります。

というわけで、当日の発表スライドです。


輪読会で出た話


「Web系システムの中でオートマトンが活用できる事例はありますか?」という質問がありました。
輪読回ではメルカリ アッテの例を取り上げていました。内容を要約すると「多くの状態が存在し遷移先が多岐に渡るため、テスタビリティとメンテナビリティを向上するためにオートマトンを導入した」例です。ここまでオートマトンが使われているコードを見たことがないので、理論とソフトウェアとの繋がりを身近に感じました。(高山さん、ありがとうございました)


「状態遷移図書く場面ってなんだろう?」という話もありました。
テストエンジニアリングの観点で、権限周りを状態遷移図で描くとテストしやすくなります。
下の記事を読んでいると、権限周りだけでなく画面のテストにおいても使えそうだと思いました。 (カルバートさん、ありがとうございました)


今後


「動的に変わっていく振る舞い」はオートマトンや状態遷移図で表現できることがわかりました。
輪読会で出たオートマトンを実装する話の他に、FluxやReduxのように状態を管理するフレームワークを使う例もあります。今後はこの仕組みについて実装しつつ理解したいと思いました。