株式会社インデペンデンスシステムズ横浜

システム開発エンジニアの西田五郎が運営しております。Raspberry Pi や Arduino その他新規開発案件のご依頼をお待ちしております。

Node.js Raspberry Pi

Raspberry PiでのNode.jsの導入(その2)Webサーバ的なプログラム

投稿日:2015年2月28日 更新日:

Raspberry PiでのNode.jsの導入の2回目です。前回はインストールと動作確認でした。今回はもう少しWebサーバ的なプログラムを作成してみます。今現在で日本のNode.jsの公式ページにもリンクがある以下のページを見ながら作成しました。合わせて参照して頂ければと思います。このページは私のようにサーバサイドのJava、PHP等の経験はあるけどサーバサイドのJavaScriptは初めてという方々にはいいかもしれないです。
Nodeビギナーズブック

ここでは上記サイトのプログラムとほぼ同じですが、もう少し簡単に、HTTPでカレントディレクトリの静的ファイルを送信する機能と、特定のURLでは何かの処理を実行するための機能があります。特にRaspberry Pi独自のGPIO等を使っていないのでパソコン等での他のLinuxディストリビューションでも動作すると思います。

ここでの処理は、たとえば、HTMLファイル、cssファイル、画像ファイルがカレントディレクトリに存在する状態で、ブラウザから以下のように表示出来ます。URLは、Raspberry PiでAvahi を使ってホスト名でアクセスするで書いたAvahiを使って、サーバ名.localでアクセスしています。もちろん、IPアドレスでもアクセス出来ました。
0001

特定のURLで以下のように表示だけですが処理出来ます。
0007

ファイルがない場合は以下のように返します。
0009

このように、Webサーバー的な機能がありますが、リクエストをマルチで処理する機能はありません。そのあたりは、Nodeビギナーズブックにブロッキングとノンブロッキングの項目から説明があります。ここではHTTP的な通信が出来ればということにしました。Node.jsでindex.jsから起動出来ます。

ここからプログラムの説明です。JavaScriptの特徴である、関数渡し(匿名関数、匿名関数)、イベント駆動コールバック等を使っていますが、このあたりの説明は省略します。必要な場合は、Nodeビギナーズブック等を参照して下さい。また私も今回初めてNode.jsを使っています。ご了承頂ければと思います。

以下のファイルを作成しました。(Nodeビギナーズブックとほぼ同じですがファイルを返す処理を追加しました。)

index.js – サーバ、ルータ、リクエストハンドラを構成してサーバを起動する。
server.js – 指定されたルータ、リクエストハンドラでHTTPサーバを起動する。
router.js – リクエストのURLからのルーティングの処理を行う。
requestHandlers.js – リクエストごとの各処理の実装。

ファイル一式は以下からダウンロード出来ます。(※単純なテスト的なプログラムです。不具合等は弊社では一切の責任を負いかねます。)
今回のスクリプトとテストファイル一式

index.jsは最後にして、server.jsから書きます。server.jsは以下です。HTTPサーバです。リクエストを受け取るとルーティングへ渡します。

var http = require("http");
var url = require("url");

function start(route, handle) {
	function onRequest(request, response) {
    		var pathname = url.parse(request.url).pathname;
    		console.log("Request for " + pathname + " received.");

		route(handle, pathname, response, request);
  	}

	http.createServer(onRequest).listen(8888);
	console.log("Server has started.");
}
exports.start = start;

router.jsは以下です。ルーティングの処理です。実際はルーティングの処理というほどでもないですが、特定の機能のURLであればその処理(ハンドラ)を設定して、それ以外は静的ファイルが指定されていればそのファイルを読み込んでハンドラへ渡します。それ以外はそれらしいHTTPステータスのハンドラへ渡します。

var fs = require("fs")

function route(handle, pathname, response, request) {
	console.log("About to route a request for " + pathname);

	if (typeof handle[pathname] === 'function') {
		handle[pathname](response, request);
	} 
	else 
	{
		pathname = "." + pathname;

		fs.exists(pathname, function(exists){
        		console.log(pathname+" "+exists);
        		
			if (!exists) { 
				handle["http404"](response); 
				return; 
			}
        		fs.readFile(pathname, "binary", function(err, file){
                		if (err) { 
					handle["http500"](err); 
					return ; 
				}
                		handle["http200"](file, response);  
        		});
		});
  	}
}
exports.route = route;

requestHandlers.js は以下です。start()がデフォルトの処理で、action1()、action2()が特定の処理を想定していて、http200()からがhttpの処理的なハンドラです。http200()でファイルを返しています。

var querystring = require("querystring")
var fs = require("fs")

var header = {
"Pragma": "no-cache",
"Cache-Control" : "no-cache"
}

function start(response) {
	console.log("Request handler 'start' was called.");

	response.writeHead(200, header);
	response.end("start\n")
}

function action1(response, request) {
	console.log("Request handler 'action1' was called.");

	response.writeHead(200, header);
	response.end("action1\n")
}

function action2(response) {
  	console.log("Request handler 'action2' was called.");

	response.writeHead(200, header);
	response.end("action2\n")
}

function http200(file,response){
	console.log("Request handler '200' was called.");

	response.writeHead(200, header);
	response.write(file, "binary");
	response.end();
}

function http404(response){
	console.log("Request handler '404' was called.");

	response.writeHead(404, header);
	response.end("http404");
}

function http500(response){
	console.log("Request handler '500' was called.");

	response.writeHead(500, header);
	response.end("http500");
}

exports.start = start;
exports.action1 = action1;
exports.action2 = action2;

exports.http200 = http200;
exports.http404 = http404;
exports.http500 = http500;

最後にindex.jsです。このindex.jsから、Node index.jsとして起動します。上記のサーバ、ルータ、リクエストハンドラを構成してサーバを起動します。

var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {};
handle["/"] = requestHandlers.start;
handle["/action1"] = requestHandlers.action1;
handle["/action2"] = requestHandlers.action2;

handle["http200"] = requestHandlers.http200;
handle["http404"] = requestHandlers.http404;
handle["http500"] = requestHandlers.http500;

server.start(router.route, handle);

プログラムは以上です。実際には特定の処理としている action1()、action2()では何もしていなので、テスト的なプログラムになっています。それでもファイルを返す場合と分岐出来たので最初としてはいいかと思っています。またマルチでのリクエスト処理も課題です。

但しですが、Node.jsの場合はWebアプリケーションを作成する場合は、Expressに代表されるフレームワークを使うのが一般的かとは思います。今回はここまでですが、またいろいろと書きたいと思います。


AdSense

AdSense

-Node.js, Raspberry Pi

執筆者:

関連記事

Raspberry Piでセンサネットワーク稼働試験(基本編)構成・課題等

Raspberry Piでセンサネットワークを構築して実際に稼働してみます。基本編としてRaspberry Piにセンサを直接接続する構成です。その構成で安定して稼働させるにはどういった課題があるか書 …

Raspberry Piをモニターとキーボードなしで導入する(その1)SSHでのログインまで

Raspberry Piをモニターとキーボードなしで導入する方法についてです。 (※2021/05/02追記 現時点で最新版のRaspberry PI OSでモニターとキーボードなしで導入する手順を書 …

Raspberry Pi 3でのコンソールケーブル利用と初期設定

Raspberry Pi 3 でコンソールケーブルを利用する方法と初期設定についてです。 以下のページ等で今までこのテーマについて書きました。 Raspberry Piをモニターとキーボードなしで導入 …

Raspberry Piでタッチスクリーン付3.5インチTFTを使ってみた

Raspberry Piでタッチスクリーン付モニタを使ってみました。PiScreenという商品名で3.5インチTFT(480×320) でタッチスクリーン付です。以下の商品です。以下は完成品 …

Raspberry PiでIoT(温度・湿度・気圧データ編 その2)Webサーバ側構築とデータ送信

Raspberry PiでIoT 温度・湿度・気圧データ編の2回目です。前回はRaspberry Piに温度・湿度・気圧データが測定出来るセンサモジュールのBME280を接続して値を取得しました。 以 …