【Node.js】Httpサーバーのルーティング

Dart
この記事は約6分で読めます。

概要

API連携でテストする必要があって、Node.jsで、モックサーバーを書いたんだけど、かなりコードが見づらいし、使いにくかった。
気軽に使えるように、標準モジュールで書いたんだけど、いまいちで、expressとかは高機能なので、気軽に使えるルーティング機能をつくってみた。

Dockerなくても動きますが、おまけで書いてます。

開発環境

  • Windows 11
  • Docker Desktop
  • Node.js 12

フォルダ構成

- 作業フォルダ
  - server.js
  - router.js
  - public フォルダ
    |- test.html 静的ファイルのサンプル

Httpモジュールの基本

内容を理解するための、基本となるコードです。
localhostの8080ポートにアクセスすると、「Hello World」が表示されます。
リクエストとレスポンスがわたってくるコールバック関数を追加していく感じです。

const http = require('http');

const server = http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World');
});

server.listen(8080);

本当はコールバック関数内で、urlのパスを取得して分岐しながら処理を記載していきます。

コード

ルーティングのコードです。
機能は、主に、ルーティング機能です。getとpostメソッドのコールバック関数を登録、静的ファイルの読み込みなどです。静的ファイルは、ルーティングに登録されていないURLで、「public」フォルダ配下にあれば読み取ります。
「bodyParser」は、POSTのリクエストボディをreq.bodyに埋め込みます。

router.js
var parser = require('url');
var fs = require('fs');
var handlers = {};

exports.get = (url, handler) => {
  handlers["GET/"+url] = handler;
}

exports.post = (url, handler) => {
  handlers["POST/"+url] = handler;
}

exports.route = (req) => {
  var url = parser.parse(req.url, true);
  var handler = handlers[req.method+"/"+url.pathname];
  if (!handler) handler = this.missing(req);
  return handler;
}

exports.missing = (req) => {
  var url = parser.parse(req.url, true);
  var path = __dirname + "/public" + url.pathname
  try {
    var data = fs.readFileSync(path);
    mime = req.headers.accepts || 'text/html'
    return (req, res) => {
      res.writeHead(200, {'Content-Type': mime});
      res.write(data);
      res.end();
    }
  } catch (e) {
    return (req, res) => {
      res.writeHead(404, {'Content-Type': 'text/plain'});
      res.write("No route registerd for " + url.pathname);
      res.end();
    }
  }
}

exports.bodyParser = (req) => {
  return new Promise((resolve, reject) => {
    if (req.method !== 'POST') {
      resolve();
      return;
    }

    var data = '';
    req.on('data', (chunk) => {
      data += chunk.toString();
    })

    req.on('end', () => {
      try {
        req.body = JSON.parse(data);
      } catch (e) {
        req.body = {};
      }
      resolve();
    });

    req.on('error', (e) => {
      reject(e);
    });
  });
}
server.js

createServerのコールバック関数で、ルーティングの処理を追加します。内容はコメント

// server.js
const http = require('http');
const router = require('./router'); // routerを読み込み

router.get('/', (req, res) => { // httpメソッドが「GET」、パスが「/」を登録
  res.writeHead(200,  {'Content-Type': 'text/html'});
  res.end('<html><body>Hello World!</body></html>')
});

const server = http.createServer(async (req, res) => {
  await router.bodyParser(req); // POSTパラメータ対応
  const handler = router.route(req); // 登録済みの関数を取得(ルーティング)
  handler(req, res); // 関数の実行
});

// start server on port 8080
server.listen(8080);

実行

server.jsを実行します。

node server.js

chromeなどのブラウザでURLを開きます。

http://localhost:8080/

おまけ

Dockerfile
FROM node:12

# アプリケーションディレクトリを作成する
WORKDIR /usr/src/app

# アプリケーションのソースをバンドルする
COPY . .

EXPOSE 8080
CMD [ "node", "server.js" ]

コメント