Expressを使ってherokuでHPを公開
まえがき
expressとは
node.jsのHTTPサーバー用のフレームワークである(本家によると高速で最小限である)、実際のHTTPモジュールはnode.jsに組み込まれている。
サーバーとしては本当に最小限でありとあるurlにアクセスがあった際に文字列(HTMLやプレーンテキスト等)を返すだけである。
herokuとは
Ruby on Railsとして開発されたPaaS(Platform as a Service)であるが、現在では様々な言語をサポートしている。
node.jsではgitを使ってpushすることでデプロイすることが可能で(他の言語デプロイ方法は知らない)、また各種OSのCLIツールが用意されておりウェブページにアクセスせずに作業できる。
環境と初期設定
ネット環境がないところでも簡単なテストぐらいが出来るようにローカルでも走る環境を作る。
gitやnodejs、heroku-cliなどはインストール済みとしてLinux(Ubuntu上)での作業を前提とする。またherokuアカウントも取得済みとする。
必要なもの
- git---大抵のLinuxにはデフォルトで入っていると思われる
- node.js---まあこれがないと始まらない
- yarn---npm(node package manager)の変わりのnode.jsのパッケージ管理システム、多分npmに読み替えても問題ない
- heroku-cli---heroku用のCLI(character user interface)、黒い画面でやり取りするためのツール、インストール方法は上のリンクから
それ以外のツールは基本的にyarn(npm)から取得する。
初期設定
yarnの初期設定
yarnを$ yarn init
で初期化する質問には適当に答えておいてもOK、内容はpackage.json
に記述される(後で書き換え可能)。
その後、expressを$ yarn add express
で追加する。これはpackage.json
ではdependencies
のキーにバージョン番号が値として保存される。
この操作で以下のようなJSONが自動生成される。
{
"dependencies": {
"express": "^4.17.1"
},
"name": "",
"version": "1.0.0",
"description": "",
"main": "index.js",
"author": "",
"license": "MIT",
"private": true
}
(バージョンなどは適宜読み替えてください)
gitの初期設定
$ git init
で初期化(.git/
ディレクトリが作成される、.
始まりは隠しファイルなので普通は表示されない)
.gitignore
に/node_modules/
を書き込む(ない場合は新規作成)、これも隠しファイル。heroku側はpackage.jsonからパッケージ情報を読み取ってインストールするのでデプロイさせる必要がないので無視させる、他に後悔する場合も同様。
herokuの設定
$ heroku login
をしてherokuにログインしておく(現在はブラウザが起動してログイン画面でログインのはず。ブラウザにログイン情報を保存してる場合は自動的にログイン、ただしブラウザ画面は立ち上がる)
$ heroku create <アプリの名前>
でアプリ(デプロイ先を作る)
これをしておくと$ git push heroku master
でローカルのマスター環境がherokuにデプロイされる(いろいろデプロイのログが流れる)。
Hello Worldの作成
いわゆるとりあえず動くモック
エントリーポイント(index.js)
いわゆるmain関数、expressを読み込んでポートを設定してListenする。ドキュメントルートにアクセスがあった場合にhello world
を返すだけのもの
const express=require('express');
const app=express();
const port=process.env.PORT || 8080;
app.get('/', (req, res)=>{ res.send('Hello, World'); });
app.listen(port, ()=>{ console.log(`Express Server Listen START at port=${port}`); });
process.env
は環境変数でありシェルで設定されているものと同じ、普通はPORTは設定されていないのでローカル環境では8080
のポートでサーバーがListen(聞いてる)状態になる(80ポートは普通のHTTPサーバーがある場合にバッティングするので避けておく)。
これでExpress Server Listen START at port=8080
のように表示されてサーバーが起動する。(表示のためにバッククォートを用いたテンプレートリテラルを使っている)
node.jsは伝統的にimport/export形式ではなくrequire形式なのでそれに則る。
起動(Procfile)とデプロイ
ローカルで起動する場合は$ node index
とnodeにプログラムを読み込ませる。
herokuではこれはProcfileというものにweb: node index
と記述する。
後はこれらのファイルをgitでaddしてcommitすることでデプロイされる環境が整うので$ git push heroku master
とherokuにpushすると向こうで勝手に起動してくれる。
ルーティングとミドルウェア
ミドルウェアの書き方
expressではミドルウェアはfunction(req, res, next)
のように書く。
reqはrequest、resはresponseでnode.jsのオブジェクトである。
nextは関数でありこれを呼ぶことにより次の処理につなげる、呼ばないと次に行かない。resのsendXXXを呼ぶとその時点でresponseが返るが、nextを呼ばないと次の処理が走らずにresponseが返らずエラーになる。
処理順は上から順に処理されルーティング(urlの場所)によって処理されていくので404 not found
を返したい場合は最後にそれ用の処理を書く。公式では以下のように書くことと紹介されている。
静的ファイル置き場
app.use(express.static('public/'))
とすることでそのままファイルを返すことが可能である。(いわゆるただの静的なHTTPサーバーと同じである)
アクセスログ解析
Requestについている情報を以下のように貯められる。ポイントはnextを呼ぶことで次の処理に渡される、ここではResponseを送らない。
const access_list=[];
module.exports=(req, res, next)=>{
const json={ date: new Date(),
method: req.method,
path: req.path,
ip: req.ip,
proxyIP: req.ips,
userAgent: req.headers['user-agent'],
};
console.log(json);
next();
}
読み込む側では下のようにルーティングする前にセットしておく。
const log=require('./src/log.js');
const port=process.env.PORT || 5050;
app.use(log);
// ルーティング処理、Responseを返す。
app.use(express.static('public'));
app.get('/', (req, res)=>{ res.redirect('/index.html'); });
// 404処理
app.use((req, res, next)=>{ res.status(404).send('Content Not Found'); });
app.listen(port, ()=>{ console.log(`Express Server Listen START at port=${port}`); });
セキュリティ関係
公式の情報によるととりあえずhelmetを入れておけとのこと
これにより、Cookie
関連とXSS
脆弱性に対して最低限のセキュリティは守られるよう
しかしExpress自体がかなり自由なサーバーモジュールっぽいので過信は厳禁だと思われる。
(そもそも低レベルモジュールでセキュリティリスクが高いことはすべきでないと言うべきか
あとがき
多機能なサーバー側JavaScriptのフレームワークであるexpressとそれを簡単にデプロイ出来るherokuを紹介した。
expressはかなり低レベルに近いと思われるので自分で管理するべき面が多くややこしいしセキュリティの担保もしんどそうである。
またherokuは簡単にデプロイで来て無料である半面マシンパワーはそれほどない(使用時間制限も厳しい)が簡単なテストとしてはいい環境と私は思っている
Discussion