Lua + Luarocks + MoonScript + Lapis (OpenResty) の環境 でサンプルプログラムを作成する

経緯

以前作成した Lua + Luarocks + MoonScript + Lapis (OpenResty) の環境 の上で、

  • フォームに入力させる
  • 入力した値を POST で飛ばして
  • その値によって出力を変化させる

という簡単なサンプルプログラムを作成したいと思います。

成果物

app.moon

lapis = require "lapis"
import respond_to from require "lapis.application"

class App extends lapis.Application
  @enable "etlua"
  [index: "/"]: =>
    -- デフォルトの文字コードが Shift-JIS で日本語を含むコンテンツだと文字化けしてしまうので、レスポンスヘッダを調整
    @res.headers["Content-Type"] = "text/html; charset=UTF-8"
    "ok"
    render: true
  [mna: "/mna"]: => status: 405
  [twardowski: "/twardowski"]: respond_to {
    before: =>
      -- デフォルトの文字コードが Shift-JIS で日本語を含むコンテンツだと文字化けしてしまうので、レスポンスヘッダを調整
      @res.headers["Content-Type"] = "text/html; charset=UTF-8"

    GET: =>
      @write redirect_to: "/mna"

    POST: =>
      "ok"
      @ip = @params.ip
      render: true
  }
  • etlua: テンプレートエンジンとして etlua を使用。使用するためにクラスの最初で @enable "etlua" の宣言をしています。
  • ルーティング:
    • 今回のルーティングは大きく3つ。
      • トップページ(/):
        • 他のルーティングも共通ですが、レスポンスヘッダに Content-Type 指定がないと Windows クライアント環境下ではデフォルトで文字コード Shift-JIS として表示されてしまうらしく、開発環境が UTF-8 で作成している都合上文字化けしてしまいます。
          • そのため、 @res.headers["Content-Type"] = "text/html; charset=UTF-8" で UTF-8 出力を明示しています。
      • 405 Method not Allowed(/mna):
        • 後述のフォームの表示結果ページは POSTメソッド でフォーム入力値が飛んで来ることを想定しています。そのため、 GETメソッド でアクセスされた場合はこのページへリダイレクトさせます。
        • また、その際 HTTPステータスコード を 405 にセットしたかったため status: 405 でセットしています。
          • ただしこれをセットすると etlua でファイルを用意してもページのレンダリングがされませんでした。
            • レンダリングさせる方法が分からなかったのですが今回はそこまで追究しませんでした。
      • 結果表示ページ(/twardowski):
        • トップページで入力された値 (サブネットマスク指定ありのIPv4アドレス) を使用して表示を変化させる。
          • respond_to ライブラリをファイル先頭でインポートして、その機能を利用してメソッドごとに処理を場合分け。
            • before: 共通内容。
              • 今回はトップページと同様文字コードの指定を追加。
            • GET: GETメソッド でアクセスされた場合。
              • 今回は先述した通り POSTメソッド のみ受け付けたいので @write redirect_to: "/mna" でエラーページへリダイレクトさせます。
            • POST: POSTメソッド でアクセスされた場合。
              • @paramsformタグ 内のフォーム、 name属性 でパラメータが渡ってくる(今回は name="ip")ので、 @params.ip で受け取ります。
              • etlua に出力する場合は @ 付き変数で参照可能にしておかなければいけないため、 @ip に再代入しています(@params.ip のままでも大丈夫そうですが記述が増えるので)。
              • サーバ側バリデーションもしたかったのですが、どうも標準機能では正規表現がなさげなので諦めて素通りさせました。実用では危険すぎますが、今回はあくまでサンプルと言うことで目を瞑りました……。

views/index.etlua

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

<div class="container my-4">
    <h2 class="display-2">Twardowski</h2>
    <p class="lead">Pan Twardowski is is a sorcerer in Polish folklore and literature.</p>
    <form action="./twardowski" method="post" class="row g-3">
        <div class="col-auto">
            <label for="ip" class="visually-hidden">IPアドレス</label>
            <input
                type="text"
                class="form-control"
                id="ip"
                name="ip"
                placeholder="192.0.2.1/24"
                pattern="^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/([\d]{1}|1[\d]{1}|2[\d]{1}|3[012])$"
            />
        </div>
        <div class="col-auto">
            <button type="submit" class="btn btn-primary mb-3">このIPアドレス出力する</button>
        </div>
    </form>
</div>

トップページのテンプレート。

  • デザインは Bootstrap の CDN 利用。
  • Lua 系の話とは関係ないですが、 HTML で inputタグ の pattern属性 で正規表現による入力値チェックができる、ということで今回検証 (そのためにあえてIPv4アドレスを想定した)。
    • “input pattern” | Can I use… Support tables for HTML5, CSS3, etc
      • 参考にした記事では Safari はまだ対応していない、とあったのですが Can I use… で確認したところ対応している模様。基本全部行けそうなのでこれはこれでありかもしれませんね。
      • ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/([\d]{1}|1[\d]{1}|2[\d]{1}|3[012])$ がそれ。
        • 192.0.2.1/8192.0.2.1/32 は通りますが 192.0.2.1/33 はNG。良さそうです。

views/twardowski.etlua

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

<div class="container my-4">
    <h2 class="display-2">Twardowski</h2>
    <p class="lead">A value of your inputting is ... <%= ip %></p>
</div>

出力側。 app.moon で再代入した変数 @ip の値をそのまま表示するだけ。


サンプルと言うことで色々ごまかしている部分はありますが、 HTTPメソッド ごとの表示切り分け等の挙動は確認できたので一応記録として残しておきます。

余談

例によって名前ですが、 Lua なので月関連ということで、ポーランドの伝説で魔力と引き換えに魂を売り渡す契約を悪魔とかわし、最後は地獄へ行く途中で月へ落とされてしまった魔術師 Twardowski (トファルドフスキ) です。この伝説に拠って、ポーランドでは月の海はトファルドフスキの姿、と言われることもあるようですね (日本だと兎が餅を搗いているとか薬を作っているとか言われているアレと同じ類)。

参考

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。