なんかよく分からんけどWebアプリをAzureにデプロイするまで (後編)

前回は開発フローの確立までをやった。今回は肝心のWebアプリの中身を作っていく。

作りたいWebアプリ

f:id:licheng:20211111230348p:plain:w500

静的なHTMLファイルのホスティング

前回はpugという謎のテンプレートからHTMLを生成するアプリを作ったが、ぶっちゃけ今回作りたいものは静的なHTMLファイルのホスティングで事足りるので、pugとかよく分からんものは使いたくない。そのような場合、最初のひな型生成の時の引数で --no-view を指定すればよいらしい。(詳しくはこちらのドキュメント)

npx express-generator myExpressApp --no-view

そうするとpugファイルを含むviewフォルダは生成されず、かわりに /public/index.html ファイルが生成される。エディタで開いてみると、ごくふつうのHTMLファイルである。こういうのでいいんだよ。

このHTMLファイルを自作のフロントエンドの index.html で置き換える。アプリを実行してブラウザで開いてみると、この index.html が表示された。これで静的なHTMLファイルのホスティングはできた。

Expressは何をしているのか?

なんか便利らしいのでExpressを使うことにしたが、こいつが何をやっていてどう使えばいいのかよく分からない。同じような思いを持った先人の記事が参考になった。

さっきの静的ファイルのホスティングに関しては生成された app.js の15行目に該当する記述がある。
(詳しくはこちらの記事)

app.use(express.static(path.join(__dirname, 'public')));

処理1: Web APIリクエストの受け付けとレスポンスの返信

今回はPOSTでJSON形式のリクエストを受け付け、JSON形式のレスポンスを返すAPIとする。

あるエンドポイントへのリクエストに対するレスポンスの返信は次のような形で書ける。(抜粋)

app.use(express.json());
app.use(express.urlencoded({ extended: false}));
app.post('/エンドポイント/', (req, res) => {
  req.body (=リクエストのボディ)を処理;
  var res_obj = レスポンスのオブジェクト;
  res.json(res_obj);
});

返信は文字列を返すなら res.send を、オブジェクトをJSON文字列に変換して返すなら res.json を使う。

またPOSTでなくGETの場合は、パスパラメータを取得するなら req.params を、クエリパラメータを取得するなら req.query を用いる。

処理2: Web APIリクエストの発行とレスポンスの受け取り

こちらも今回はPOSTでJSON形式のリクエストを発行し、JSON形式のレスポンスを受け取るAPIとする。

requestパッケージを用いてWeb APIリクエスト処理を実装する。じつはこの request というのはすでに非推奨のパッケージらしいのだが、ひとまずこれで実装することにする。

インストールは下記のコマンドでおこなう。ちなみに npm はデフォルトではカレントディレクトリの下の node_modules ディレクトリにパッケージをローカルインストールする。グローバルにインストールしたい場合にはオプション -g を指定する。ローカルにインストールしたパッケージに関する情報は package.json と package-lock.json に記録される。

npm install request

Web APIリクエスト処理は次のような形で書ける。(抜粋)

const request = require('request')

var req_obj = リクエストのオブジェクト;
const options = {
    method: 'POST',
    url: 'https://Web APIサーバのURL',
    headers: {
      'Content-type': 'application/json',
      'Authorization': 'API認証キー',
    },
    json: req_obj
}
request(options, (error, response, body) => {
    body (=レスポンスのボディ)を処理;
});

Web APIの中継 = 処理1 + 処理2

前述の処理1と処理2を合体させればWeb APIの中継サーバができる。
これは次のような形になる。(抜粋)

const request = require('request')

app.use(express.json());
app.use(express.urlencoded({ extended: false}));
app.post('/エンドポイント/', (req, res) => {
    const options = {
        method: 'POST',
        url: "https://Web APIサーバのURL",
        headers: {
          'Content-type': 'application/json',
          'Authorization': 'API認証キー',
        },
        json: req.body
    };
    request(options, (error, response, body) => {
        res.send(body);
    });
});

これらのコードは下記の記事を参考にした。

整理

app.js を見て不要そうなものを削除する。

  • public/style.css は使わないなら削除する。
  • indexRouter とか usersRouter とかは今回は不要っぽいので関連する記述を削除し、
    routesフォルダのファイルはフォルダごと削除する。
  • cookieParser も今回はクッキーを使用しないので不要っぽいので関連する記述を削除し、
    下記のコマンドで cookie-parser をアンインストールする。
npm uninstall cookie-parser

これでほぼミニマムな構成になった。

バージョン管理について

前述のように node_modules はnpmのパッケージをローカルインストールするフォルダなのでバージョン管理から除外する。さもないと大量のファイルがリポジトリに入ってしまう。必要なパッケージに関する情報は前述のように package.json と package-lock.json に記録されているので、この2つはバージョン管理に含める。

また、.vscode/settings.json をバージョン管理に含めるか否かは運用しだいかなと思う。Azure App Serviceのフォルダの場合、.vscode/settings.json の中にデプロイ先の情報が記述されているので要注意。

.gitignoreの例

node_modules/
.vscode/

リモート環境へのSSHログイン

デプロイしたAzureのリモート環境にSSHでログインしてコマンド操作したい場合、ブラウザで下記のようなURLにアクセスする。するとブラウザ上でターミナルが開く。(詳しくはこちらのドキュメント)

https://アプリ名.scm.azurewebsites.net/webssh/host

所感

  • 関数型っぽい書き方に慣れが必要。(本質的にはC言語の関数ポインタでコールバック関数を渡すのと同じことなのだけども。)
  • JavaScriptは関数の引数にも戻り値にも型宣言が無いので、どんな形で渡してどんな形で返ってくるか分からず困ることが多い。