Node.jsでExpressを使わずシンプルなhttpサービスを作成してみる

## 概要

Node.js でWebサービスを立ち上げる場合、Express などのパッケージを利用するのが一般的です。でもこれらのパッケージは高機能すぎて、ちょっとしたページを表示したいだけの時にはディスク容量とメモリを使い過ぎている気がします。

今回は最低限の機能をもつhttpサービスを自分で作成してみます。ちょっとしたページ公開に使用してもいいですし、必要な部分だけ拡張してそのまま自身のアプリに組み込むのもアリかもしれません。

でも一番の理由は、作るの面白そうだから、です。これ一番大事！

## 元になるプログラム

Node.js の一番シンプルなサンプルプログラムとして [IBM Bluemixで最もシンプルなNode.js環境を作成してみる](http://qiita.com/yamachan360/items/12545f3277fc0c65d2f8) で作成した、以下の簡単なアプリがあります。

```js
var http = require("http");

var server = http.createServer(function (req, res) {
    res.writeHead(200, {"Content-Type": "text/plain"});
    res.end("hello, Node.js world!");
});

var port = process.env.PORT || 3000;
server.listen(port, function() {
    console.log("To view your app, open this link in your browser: http://localhost:" + port);
});
```

ブラウザでアクセスすると、こんな感じでテキストを表示するだけのサンプルです。

![サンプル実行の様子](https://camo.qiitausercontent.com/10583063454ac917a539c2d8371632b765ff9bf2/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3139333531392f35366362616436352d393936302d306331612d343637312d6532626562323265376334642e706e67)

これを拡張して簡単なhttpサービスを実装してみましょう。

## まずはhtmlを表示

さきほどのサンプルの server を生成している部分を以下のコードで置き換えます。

```js
var fs = require('fs');

var server = http.createServer(function (req, res) {
  var url = "public" + req.url;
  if (fs.existsSync(url)) {
    fs.readFile(url, (err, data) => {
      if (!err) {
        res.writeHead(200, {"Content-Type": "text/html"});
        res.end(data);
      }
    });
  }
});
```

まず最初に、要求された req.url の前に "public" という文字列を追加しています。

これなのですが、今回のhttpサービスで使用する公開ファイルは public というサブフォルダに配置することにします。これは将来、Express に移行した時にそのまま動作するように、Express の一般的な使われ方を真似しています。

その後、ファイル操作用の fs モジュールの existsSync() 関数を使って、その指定されたファイルが実際に存在するかどうかをチェックしています。まだ作りかけですので、ここでは必ずファイルが存在するとして処理を進めましょう。

ファイルが存在すれば、それを fs.readFile() 関数を使って読み込みます。読み込みが完了すれば第二引数に指定した関数が呼ばれますので、その呼ばれた関数のなかで ContentType を text/html に設定し、読み込んだデータを書き出して終了です。

さて実際に試してみましょう。

プロジェクトの配下に public フォルダを作成し、index.html というファイル名で以下の内容を入力してください。

```html
<!doctype html>
<html lang="ja">
  <head>
    <title>simple-nodejs top</title>
  </head>
  <body>
    <p>Hello, <b>Node.js</b> world!</p>
  </body>
</html>
```

そして node server.js コマンドでアプリケーションを実行します。

![node server.js](https://camo.qiitausercontent.com/eaade041cb7625e2ec1a70f607ac0337397cad16/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3139333531392f66636531393962362d313465662d636132302d393035632d3937633638393166656565352e706e67)

ブラウザでアクセスすると、以下のようにWebページが表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/7edf3a20-193a-4a34-ed67-260ff8951d10.png)

ものすごくシンプルですが、Webサーバーが動作しました！

## ファイル名の省略に対応する

今のアプリだと、トップページを表示するのに index.html を必ず指定しなければなりません。でもこれ、省略できるのが普通ですよね？

さきほどの server.js のうち、url を定義しているところを以下に差し替えてください。

```js
var url = "public" + (req.url.endsWith("/") ? req.url + "index.html" : req.url);
```

これはわりと単純な仕組みで、要求されたurlの最後が "/" 文字であれば、"index.html"を追加する、という処理になっています。

アプリケーションを再起動してください。すると今度は、index.html を省略しても表示できるようになりました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/35b4c763-49e1-36a2-b418-8f1cb54b4c6a.png)

## 画像などの表示に対応する

html ファイルだけではWebサーバーとしては寂しいですね。画像やcssファイルなどにも対応させてみましょう。

まずは以下の関数定義を server.js ファイルに追加します。

```js
function getType(_url) {
  var types = {
    ".html": "text/html",
    ".css": "text/css",
    ".js": "text/javascript",
    ".png": "image/png",
    ".gif": "image/gif",
    ".svg": "svg+xml"
  }
  for (var key in types) {
    if (_url.endsWith(key)) {
      return types[key];
    }
  }
  return "text/plain";
}
```

Webサーバーは送信するファイルの種類に応じて、適切な ContentType を指定しなければなりません。この関数は、送信されるファイルの末尾にある拡張子を調べ、事前に登録された ContentType があればそれを返します。

その下にある res.writeHead() 関数の引数で、この定義した関数を使用するように変更します。

```js
res.writeHead(200, {"Content-Type": getType(url)});
```

さて、これで拡張できましたので、テストしてみましょう。

今回は、public フォルダの下に更に i フォルダを作成し、そこに yamachan.png というファイルを配置しました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/36bedd7a-2626-957c-f2a9-ad035d70e2d9.png)

そして public フォルダに配置した index.html の中身を以下のように書き換えました。bodyの最初にimgタグを追加しています。

```html
<!doctype html>
<html lang="ja">
  <head>
    <title>simple-nodejs top</title>
  </head>
  <body>
    <img src="i/yamachan.png"/>
    <p>Hello, <b>Node.js</b> world!</p>
  </body>
</html>
```

さて、アプリを再起動してアクセスしてみましょう。以下のように画像付きでWebページが表示されました！

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/3cd08b5c-e123-0e81-a300-33b593567a27.png)

## 最後はエラー処理

これで最低限の機能は実装できたと思うので、後はエラー処理を追加して終わりにしましょう。

Webサーバーは存在しないファイルを指定されたとき、404エラーを返す必要があります。またファイルが読み込めないなど障害が発生するかもしれませんので、その場合は500エラーを返したほうが良いでしょう。

これらのエラー処理を組み込んだ最終的なコードは以下になります。

```js
var http = require("http");
var fs = require('fs');

function getType(_url) {
  var types = {
    ".html": "text/html",
    ".css": "text/css",
    ".js": "text/javascript",
    ".png": "image/png",
    ".gif": "image/gif",
    ".svg": "svg+xml"
  }
  for (var key in types) {
    if (_url.endsWith(key)) {
      return types[key];
    }
  }
  return "text/plain";
}

var server = http.createServer(function (req, res) {
  var url = "public" + (req.url.endsWith("/") ? req.url + "index.html" : req.url);
  if (fs.existsSync(url)) {
    fs.readFile(url, (err, data) => {
      if (!err) {
        res.writeHead(200, {"Content-Type": getType(url)});
        res.end(data);
      } else {
        res.statusCode = 500;
        res.end();
      }
    });
  } else {
    res.statusCode = 404;
    res.end();
  }
});

var port = process.env.PORT || 3000;
server.listen(port, function() {
  console.log("To view your app, open this link in your browser: http://localhost:" + port);
});
```

fs.existsSync() 関数でファイルが見つからなかった場合は404エラーを、また fs.readFile() 関数で読み込みにエラーが発生した場合は500エラーを、それぞれ返す処理が追加されているのがわかります。

アプリを再起動して、存在しないアドレスを指定し、404エラーになることを確認しましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/41cc934f-fbc8-5909-a419-3a444c976bef.png)

さあこれであなたは、Node.js 上に実装されたシンプルなhttpサーバーを手にいれました。これは1Kbytesもない短いアプリケーションですが、いろいろ拡張して遊べる楽しい玩具です。

## ダウンロード

今回のプロジェクトの全5ファイルを [zipにまとめました](https://github.com/yamachan/dw/raw/master/archive/simple-nodejs_20170914.zip) ので、必要でしたらダウンロードしてお使いください！

## Bluemix環境にpushしてみる (おまけ)

今回のプロジェクトは前回の投稿 [IBM Bluemixで最もシンプルなNode.js環境を作成してみる](http://qiita.com/yamachan360/items/12545f3277fc0c65d2f8) を引き継いでいます。

せっかくなので cf push してBluemix環境で動作させてみましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/7a836060-edd1-c365-41f5-f968f9e9d2f8.png)

当然なのですが、Bluemix環境でも問題なく動作しました。これで開発したアプリを他の人に見てもらえますね。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/9ad9f082-109b-467d-0c4f-d8dcd5f9305f.png)

## ライセンス

この投稿に含まれる私の作成した全てのコードは [Creative Commons Zero](https://creativecommons.org/publicdomain/zero/1.0/deed.ja) ライセンスとします。自由にお使いください。

## Enjoy!

以上、シンプルで、でも自分で使うには十分な機能をもつ http サービスを実装してみました。Content-Typeを追加したり、どこかのREST APIと連携させたり、いろいろ機能を追加して遊んでみてください。

ではまた！