Node.jsでhttpサーバーを立ち上げる。
環境
Windows10(64bit)
nvm(Node Version Manager)を使用する。
nvmはnodeとnpmのバージョンの切り替えができる。
node.jsはアップデートが頻繁に行われるため、バージョン管理ツールを使うと都合が良いことが多い。
「特定のプロジェクトに必要なNode.jsのバージョンを簡単にインストールして使用することができる。」や「プロジェクト毎にNode.jsのバージョンの切り替えが簡単になる。」等のメリットがある。
nvm 1.1.10
node v18.15.0
npm 9.5.0
httpサーバーを立ち上げるコード「http.js」を次に示す。
現地時間を取得するために自作モジュールLocalTime.jsを使用している。
(function() {
// 自作モジュールLocalTime.jsをrequireを使ってインポートして、定数localTimeに格納する。
const localTime = require("./LocalTime.js");
// LocalTimeの言語を日本語に設定
localTime.SetLang("JP");
// LocalTimeのタイムゾーンを東京(TYO)に設定
localTime.SetTimeZone("TYO");
// Node.jsのhttpモジュールをrequireを使ってインポートして、定数httpに格納する。
const http = require("http");
const port = 3000;//port番号
const hostname = 'localhost'; // ホスト名またはドメイン名
// httpオブジェクトからcreateServerメソッドを実行する。
// createServerメソッドは、引数としてリクエストを処理するコールバック関数を受け取る。
// このコールバック関数は、リクエストが発生した際に実行され、リクエストオブジェクト(req)とレスポンスオブジェクト(res)を受け取る
// つまり、このコールバック関数は、http://localhost:3000にアクセスしたときに実行される。
// このコールバック関数内で、適切なレスポンス(res)を生成してクライアントに送信する処理を実装する。
// リクエストオブジェクト(req)は、http.IncomingMessageオブジェクトのインスタンスである。
// リクエストオブジェクト(req)は、HTTPリクエスト(リクエストヘッダーやボディ)のデータを読み取るためのデータを読み取るためのReadableストリームです。
// レスポンスオブジェクト(res)は、http.ServerResponseオブジェクトのインスタンスである。
// レスポンスオブジェクト(res)は、Writableストリームとして機能する。
// レスポンスオブジェクト(res)は、クライアントに対してデータを送信するためのストリームとして扱われる。
// レスポンスオブジェクト(res)はhttp.ServerResponseオブジェクトのインスタンスであり、HTTPレスポンスを生成および送信するための
// Writableストリームです。レスポンスオブジェクト(res)に対して書き込まれたデータはクライアントに送信されます。
// writeメソッドやendメソッドを使用してレスポンスボディを書き込むことができます。
// レスポンスオブジェクト(res)のボディに書き込むにはwriteメソッドを使う。
// レスポンスオブジェクト(res)の送信を完了するためにはendメソッドを使う。
const server = http.createServer((req, res) => {
console.log(
`Executed. \n` +
`${localTime.GetLocalTimeString()} \n` +
`${req.method} \n` +
`${req.url} \n` +
`${req.headers["user-agent"]}`
);
const url = `http://${req.headers.host}`;
// URL解析
// req.urlは相対URLが入る。
// urlはベースurl(相対URLを解決するための基準となるURL)が入る。
const myURL = new URL(req.url, url);
const whitespace = 2;
const result = [];
const names = [];
let stringLengthMax = 0;
let buf = ' ';
let blanks = '';
console.log("");
result.push(myURL.href);
result.push(myURL.origin);
result.push(myURL.protocol);
result.push(myURL.username);
result.push(myURL.password);
result.push(myURL.host);
result.push(myURL.port);
result.push(myURL.hostname);
result.push(myURL.pathname);
result.push(myURL.hash);
result.push(myURL.search);
names.push("href");
names.push("origin");
names.push("protocol");
names.push("username");
names.push("password");
names.push("host");
names.push("port");
names.push("hostname");
names.push("pathname");
names.push("hash");
names.push("search");
// names配列の中の文字列で最大長をstringLengthMaxに格納する。
names.forEach(t => {
if (t.length > stringLengthMax) stringLengthMax = t.length;
});
// bufに格納した空の文字列を「stringLengthMax + whitespace」回数分のコピーを含む新しい文字列blanksを作る。
blanks = buf.repeat(stringLengthMax + whitespace);
// URLの解析結果を出す。
names.forEach((t, u) => (console.log(t + ':' + blanks.slice(t.length) + result[u])));
console.log("");
console.log("req.headers = ");
console.log(req.headers);
console.log("");
let str = [];
str.push("method:");
str.push(req.method);
str.push("\n");
str.push("req.url:");
str.push(req.url);
str.push("\n");
if (myURL.pathname === "/") {
// ドキュメントルート(/)がリクエストさた場合
res.writeHead(200, {
'Content-Type': 'text/plain;charset=utf-8'
});
if (req.method === "POST") {
let i = 0
// data受信イベントの発生時に断片データ(chunk)を取得する。
req.on('data', chunk => {
console.log(localTime.GetLocalTimeString() + " i = " + i + ", chunk = " + chunk + "");
i += 1;
str.push(chunk);
});
// 受信完了(end)イベント発生時
//「end」イベントは、レスポンスのデータの読み取りが完了した場合に発生する。
req.on('end', function() {
str.push("\n");
//レスポンスデータをchunk分割する。
res.write(str.join(""));
res.end("Hello World! " + req.method + "\n");
});
} else if (req.method === "GET") {
// URLSearchParamsに含まれる値はforEach()メソッドで取り出し可能である。
myURL.searchParams.forEach(function(value, key) {
str.push(key + " = " + value + "\n");
console.log(localTime.GetLocalTimeString() + ", " + key + " = " + value);
});
// レスポンスデータをchunk分割する。
res.write(str.join(""));
res.end("Hello World! " + req.method + "\n");
} else {
let i = 0
// data受信イベントの発生時に断片データ(chunk)を取得する。
req.on('data', chunk => {
console.log(localTime.GetLocalTimeString() + " i = " + i + ", chunk = " + chunk + "");
i += 1;
str.push(chunk);
});
// 受信完了(end)イベント発生時
// 「end」イベントは、レスポンスのデータの読み取りが完了した場合に発生する。
req.on('end', function() {
str.push("\n");
//レスポンスデータをchunk分割する。
res.write(str.join(""));
res.end("Hi! " + req.method);
});
}
} else {
// ドキュメントルート(/)がリクエストされない場合はメソッドに関係なくここを通る。
res.writeHead(404, {
'Content-Type': 'text/plain;charset=utf-8'
});
// レスポンスデータをchunk分割する。
res.write(str.join(""));
res.end("404 Not Found");
}
});
// serverインスタンスオブジェクトからlistenメソッドを実行する。
// httpサーバーが指定されたポート(3000)でリクエストを受け付けるようになる。
server.listen(port, hostname, () => {
console.log(localTime.GetLocalTimeString() + ` Server running at http://${hostname}:${port}`);
});
})();
http.jsを実行して、firefoxで「http://localhost:3000」にアクセスすると次のようになる。
c:\node\server>node http.js
[TYO] 2023-05-27(Sat) 20:30:38.485 Server running at http://localhost:3000
Executed.
[TYO] 2023-05-27(Sat) 20:31:04.387
GET
/
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
href: http://localhost:3000/
origin: http://localhost:3000
protocol: http:
username:
password:
host: localhost:3000
port: 3000
hostname: localhost
pathname: /
hash:
search:
req.headers =
{
host: 'localhost:3000',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'accept-language': 'ja,en-US;q=0.7,en;q=0.3',
'accept-encoding': 'gzip, deflate, br',
connection: 'keep-alive',
cookie: '_ga=GA1.1.1823735261.1646346341',
'upgrade-insecure-requests': '1',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'none',
'sec-fetch-user': '?1'
}