HTTP Server

この章ではHTTP Serverの基本的な作り方を学びます.

Hello World

まずはHello Worldします.

1
2
3
4
5
6
7
var http = require('http');
var server = http.createServer(function(req, res){
   res.writeHead(200, {'Content-Type': 'text/plain'});
   res.write('Hello World\n');
   res.end();
});
server.listen(3000);

これだけです.

http.createServer(function(req, res){ /* */ }} で httpServer インスタンスを作成し,所定のポートでlistenするだけです.

リクエストの処理もイベント

サーバーインスタンス自体がEventEmitterオブジェクトなので、次のように,リクエスト処理を分割して記述できます.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
var http = require('http');

var server = http.createServer(function(req, res){
   res.writeHead(200, {
      'Content-Type': 'text/plain'
   });
   res.write('Hello World\n');
   res.end();
});

server.on('request', function(req, res){
   console.log(req.url + ' "' + req.headers['user-agent'] + '"');
});

server.listen(3000);

便利でしょう?

ちなみに,

var server = http.createServer(function(req, res){  });

は,以下と等価です.

var server = http.createServer();
server.on('request', function(req, res){  });

非同期メソッドを使う

リクエストの処理中に非同期イベントを発生させることは問題ありません. しかるべきタイミングで response.writeHead(), response.end() を呼び出せば正しくコンテンツを返すことができます.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
var http = require('http');
var server = http.createServer(function(req, res){
   setTimeout(
      function(){
         res.writeHead(503, {'Content-Type': 'text/plain'});
         res.write("I'm busy\n");
         res.end();
      },
      1000
   );
});
server.listen(3000);

たとえばファイルをホストする場合は次のようにできます.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
var http = require('http'),
    fs = require('fs');
var server = http.createServer(function(req, res){
   var read = fs.createReadStream('/tmp/helloworld.html');
   var head = true;
   read.on('error', function(){
      res.writeHead(500, {'Content-Type': 'text/plain'});
      res.write('IO Error\n');
      res.end();
   });
   read.on('data', function(data){
      if( head ){
         res.writeHead(200, {'Content-Type': 'text/plain'});
         head = false;
      }
      res.write(data);
   });
   read.on('end', function(data){
      res.end();
   });
});
server.listen(3000);

/tmp/helloworld.html にファイルを置かない場合は 500 Error になりますが,置けばそのファイルがレスポンスとして送信されます.

HTTP POST を扱う

request イベントで渡される http.serverRequest オブジェクトは読み込み可能なストリームです.この serverRequest オブジェクトの data イベントを使うことで POST データの読み取りをすることができます.

以下は簡単なEcho HTTP Serverの例です.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
var http = require('http');
var server = http.createServer(function(req, res){
   if( req.method == 'POST' ){
      res.writeHead(200, {
         'Content-Type': req.headers['content-type']
      });
      req.on('data', function(data){
         res.write(data);
      });
      req.on('end', function(){
         res.end();
      });

   }else{
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.write('Hello World\n');
      res.end();
   }

});
server.listen(3000);

テンプレートエンジンを使う

最後に,テンプレートエンジンを使ってみます. node.js はJavaScriptが動作しますから,JavaScript で実装されているものであれば何でもかまいません [1]

EJS

EJS は Ruby の ERB のような実装のテンプレートエンジンです.

$ npm install ejs

これでEJSが使えるようになりますので,アプリケーションに組み込んでみます.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
var http = require('http'),
    fs = require('fs'),
    querystring = require('querystring'),
    ejs = require('ejs');

function extractParams(url){
   return querystring.parse(url.split('?')[1] || "");
}

var server = http.createServer(function(req, res){
   var template = fs.readFileSync(__dirname + '/template.ejs');
   var params = extractParams(req.url);
   res.writeHead(200, {'Content-Type': 'text/plain'});
   res.write(ejs.render(template.toString(),  {
      locals: {
         name: params.name
      }
   }));;
   res.end();
});
server.listen(3000);

このプログラムと同じディレクトリに template.ejs ファイルを用意します.

<html>
  <body>
    <p>
      Hello <%= name %>
    </p>
  </body>
</html>

その他のテンプレートエンジン

筆者的には https://github.com/janl/mustache.js がお気に入りです.

フレームワーク

Webアプリケーションを開発するためには,いわゆるフレームワークと呼ばれるものが必須の時代になってきています.

node.js の上でもいくつかのWebフレームワークが開発されています [2]

Express
Ruby Sintra にインスパイアされたフレームワークです. Connect と呼ばれるミドルウェアフレームワークの上に,リクエストルーティングやセッション管理,テンプレートエンジンを組み込んだ構成になっています.
Geddy
Merb,Rails,Pylons,あるいは Django に似たような機能を提供するフレームワーク,とのことです.
[1]DOMを使うものでもjsdom と呼ばれるライブラリを使えば(苦労はするものの)大抵は動きます.
[2]https://github.com/ry/node/wiki/modules