HTTPモジュールを使用してNode.jsでWebサーバーを作成する方法
著者はCOVID-19救済基金を選択し、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
ブラウザでWebページを表示すると、インターネット上の別のコンピュータに要求が送信され、応答としてWebページが提供されます。 あなたがインターネット経由で話しているそのコンピュータはウェブサーバーです。 Webサーバーは、ブラウザーなどのクライアントからHTTP要求を受信し、HTMLページやAPIからのJSONなどのHTTP応答を提供します。
サーバーがWebページを返すには、多くのソフトウェアが関係しています。 このソフトウェアは通常、フロントエンドとバックエンドの2つのカテゴリに分類されます。 フロントエンドコードは、ナビゲーションバーの色やテキストのスタイルなど、コンテンツの表示方法に関係しています。 バックエンドコードは、データの交換、処理、および保存の方法に関係しています。 ブラウザからのネットワークリクエストを処理したり、データベースと通信したりするコードは、主にバックエンドコードによって管理されます。
Node.js を使用すると、開発者は JavaScript を使用してバックエンドコードを記述できますが、従来はブラウザーでフロントエンドコードを記述して使用されていました。 このようにフロントエンドとバックエンドの両方を一緒に使用すると、Webサーバーの作成にかかる労力が軽減されます。これが、Node.jsがバックエンドコードを作成するための一般的な選択肢である主な理由です。
このチュートリアルでは、Node.jsに含まれているhttpモジュールを使用してWebサーバーを構築する方法を学習します。 JSONデータ、CSVファイル、およびHTMLWebページを返すことができるWebサーバーを構築します。
前提条件
- Node.jsが開発マシンにインストールされていることを確認します。 このチュートリアルでは、Node.jsバージョン10.19.0を使用します。 これをmacOSまたはUbuntu18.04にインストールするには、Node.jsをインストールしてmacOSにローカル開発環境を作成する方法またはのPPAを使用したインストール]セクションの手順に従います。 Ubuntu18.04にNode.jsをインストールする方法。
- Node.jsプラットフォームは、すぐに使用できるWebサーバーの作成をサポートしています。 開始するには、Node.jsの基本に精通していることを確認してください。 Node.jsで最初のプログラムを作成して実行する方法に関するガイドを確認することから始めることができます。
- また、セクションの1つに非同期プログラミングを利用しています。 Node.jsでの非同期プログラミングやファイルを操作するための
fs
モジュールに慣れていない場合は、Node.jsで非同期コードを作成する方法に関する記事で詳細を学ぶことができます。 。
ステップ1—基本的なHTTPサーバーを作成する
プレーンテキストをユーザーに返すサーバーを作成することから始めましょう。 これは、サーバーのセットアップに必要な主要な概念をカバーし、JSONなどのより複雑なデータ形式を返すために必要な基盤を提供します。
まず、演習や記事の他の演習を行うために、アクセス可能なコーディング環境をセットアップする必要があります。 ターミナルで、first-servers
というフォルダーを作成します。
mkdir first-servers
次に、そのフォルダに入ります。
cd first-servers
次に、コードを格納するファイルを作成します。
touch hello.js
テキストエディタでファイルを開きます。 ターミナルで利用できるnano
を使用します。
nano hello.js
まず、すべてのNode.jsインストールで標準のhttp
モジュールをロードします。 hello.js
に次の行を追加します。
first-servers / hello.js
const http = require("http");
http
モジュールには、サーバーを作成する関数が含まれています。これについては後で説明します。 Node.jsのモジュールについて詳しく知りたい場合は、Node.jsモジュールの作成方法の記事をご覧ください。
次のステップは、サーバーがバインドされるホストとポートの2つの定数を定義することです。
first-servers / hello.js
... const host = 'localhost'; const port = 8000;
前述のように、Webサーバーはブラウザーや他のクライアントからの要求を受け入れます。 DNSサーバーによってIPアドレスに変換されるドメイン名を入力することにより、Webサーバーと対話する場合があります。 IPアドレスは、インターネットなどのネットワーク上のマシンを識別する一意の一連の番号です。 ドメイン名の概念の詳細については、 DNSの用語、コンポーネント、および概念の概要の記事を参照してください。
値localhost
は、コンピューターが自分自身を参照するために使用する特別なプライベートアドレスです。 これは通常、内部IPアドレス127.0.0.1
と同等であり、ローカルコンピューターでのみ使用でき、参加しているローカルネットワークやインターネットでは使用できません。
ポートは、サーバーがIPアドレスへのエンドポイントまたは「ドア」として使用する番号です。 この例では、Webサーバーにポート8000
を使用します。 ポート8080
および8000
は通常、開発のデフォルトポートとして使用され、ほとんどの場合、開発者はHTTPサーバーの他のポートではなくこれらを使用します。
サーバーをこのホストとポートにバインドすると、ローカルブラウザでhttp://localhost:8000
にアクセスしてサーバーにアクセスできるようになります。
Node.jsでリクエストリスナーと呼ぶ特別な関数を追加しましょう。 この関数は、着信HTTP要求を処理し、HTTP応答を返すことを目的としています。 この関数には、要求オブジェクトと応答オブジェクトの2つの引数が必要です。 リクエストオブジェクトは、着信するHTTPリクエストのすべてのデータをキャプチャします。 応答オブジェクトは、サーバーのHTTP応答を返すために使用されます。
最初のサーバーは、誰かがアクセスするたびに"My first server!"
というメッセージを返すようにします。
次にその関数を追加しましょう:
first-servers / hello.js
... const requestListener = function (req, res) { res.writeHead(200); res.end("My first server!"); };
関数は通常、その機能に基づいて名前が付けられます。 たとえば、本のリストを返すリクエストリスナー関数を作成した場合、listBooks()
という名前を付ける可能性があります。 これはサンプルケースであるため、一般名requestListener
を使用します。
Node.jsのすべてのリクエストリスナー関数は、req
とres
の2つの引数を受け入れます(必要に応じて、別の名前を付けることができます)。 ユーザーが送信するHTTPリクエストは、最初の引数req
に対応するRequestオブジェクトにキャプチャされます。 ユーザーに返すHTTP応答は、2番目の引数res
でResponseオブジェクトと対話することによって形成されます。
最初の行res.writeHead(200);
は、応答のHTTPステータスコードを設定します。 HTTPステータスコードは、HTTPリクエストがサーバーによってどの程度適切に処理されたかを示します。 この場合、ステータスコード200
は"OK"
に対応します。 Webサーバーが意味する意味で返すことができるさまざまなHTTPコードについて知りたい場合は、一般的なHTTPエラーコードのトラブルシューティング方法に関するガイドから始めることをお勧めします。
関数の次の行res.end("My first server!");
は、HTTP応答を要求したクライアントに書き戻します。 この関数は、サーバーが返さなければならないすべてのデータを返します。 この場合、テキストデータを返します。
最後に、サーバーを作成してリクエストリスナーを利用できるようになりました。
first-servers / hello.js
... const server = http.createServer(requestListener); server.listen(port, host, () => { console.log(`Server is running on http://${host}:${port}`); });
CTRL+X
を押して、nano
を保存して終了します。
最初の行では、http
モジュールのcreateServer()
関数を使用して、新しいserver
オブジェクトを作成します。 このサーバーはHTTPリクエストを受け入れ、それらをrequestListener()
関数に渡します。
サーバーを作成したら、それをネットワークアドレスにバインドする必要があります。 server.listen()
メソッドを使用してこれを行います。 port
、host
、およびサーバーがリッスンを開始したときに起動するコールバック関数の3つの引数を受け入れます。
これらの引数はすべてオプションですが、Webサーバーで使用するポートとホストを明示的に指定することをお勧めします。 Webサーバーをさまざまな環境に展開する場合、負荷分散または DNS エイリアスを設定するには、Webサーバーが実行されているポートとホストを知っている必要があります。
コールバック関数はコンソールにメッセージを記録するので、サーバーがいつ接続をリッスンし始めたかを知ることができます。
注: requestListener()
はreq
オブジェクトを使用しませんが、それでも関数の最初の引数である必要があります。
15行未満のコードで、Webサーバーができました。 実際の動作を確認し、プログラムを実行してエンドツーエンドでテストしてみましょう。
node hello.js
コンソールに、次の出力が表示されます。
OutputServer is running on http://localhost:8000
プロンプトが消えることに注意してください。 これは、Node.jsサーバーが長時間実行されるプロセスであるためです。 クラッシュして終了するエラーが発生した場合、またはサーバーの実行中のNode.jsプロセスを停止した場合にのみ終了します。
別のターミナルウィンドウで、ネットワークとの間でデータを転送するためのCLIツールであるcURLを使用してサーバーと通信します。 次のコマンドを入力して、実行中のサーバーにHTTPGET
リクエストを送信します。
curl http://localhost:8000
ENTER
を押すと、端末に次の出力が表示されます。
OutputMy first server!
これでサーバーがセットアップされ、最初のサーバー応答が得られました。
サーバーをテストしたときに何が起こったのかを分析してみましょう。 cURLを使用して、GET
リクエストをhttp://localhost:8000
のサーバーに送信しました。 Node.jsサーバーは、そのアドレスからの接続をリッスンしました。 サーバーはその要求をrequestListener()
関数に渡しました。 この関数は、ステータスコード200
のテキストデータを返しました。 次に、サーバーはその応答をcURLに送り返し、cURLは端末にメッセージを表示しました。
続行する前に、CTRL+C
を押して実行中のサーバーを終了しましょう。 これにより、サーバーの実行が中断され、コマンドラインプロンプトに戻ります。
アクセスするほとんどのWebサイトまたは使用するAPIでは、サーバーの応答がプレーンテキストで表示されることはめったにありません。 一般的な応答形式としてHTMLページとJSONデータを取得します。 次のステップでは、Webで遭遇する一般的なデータ形式でHTTP応答を返す方法を学習します。
ステップ2—さまざまな種類のコンテンツを返す
Webサーバーから返される応答は、さまざまな形式をとることができます。 JSONとHTMLについては前に説明しましたが、XMLやCSVなどの他のテキスト形式を返すこともできます。 最後に、Webサーバーは、PDF、zipファイル、オーディオ、ビデオなどの非テキストデータを返すことができます。
この記事では、返されたプレーンテキストに加えて、次の種類のデータを返す方法を学習します。
- JSON
- CSV
- HTML
3つのデータ型はすべてテキストベースであり、Web上でコンテンツを配信するための一般的な形式です。 多くのサーバー側の開発言語とツールは、これらのさまざまなデータ型を返すことをサポートしています。 Node.jsのコンテキストでは、次の2つのことを行う必要があります。
- HTTP応答の
Content-Type
ヘッダーに適切な値を設定します。 res.end()
が正しい形式でデータを取得することを確認してください。
いくつかの例を使って、これを実際に見てみましょう。 このセクションで作成するコードとそれ以降のコードは、以前に作成したコードと多くの類似点があります。 ほとんどの変更はrequestListener()
関数内に存在します。 この「テンプレートコード」を使用してファイルを作成し、今後のセクションをわかりやすくします。
html.js
という名前の新しいファイルを作成します。 このファイルは、後でHTTP応答でHTMLテキストを返すために使用されます。 ここにテンプレートコードを配置し、さまざまなタイプを返す他のサーバーにコピーします。
ターミナルで、次のように入力します。
touch html.js
次に、このファイルをテキストエディタで開きます。
nano html.js
「テンプレートコード」をコピーしてみましょう。 nano
にこれを入力します。
first-servers / html.js
const http = require("http"); const host = 'localhost'; const port = 8000; const requestListener = function (req, res) {}; const server = http.createServer(requestListener); server.listen(port, host, () => { console.log(`Server is running on http://${host}:${port}`); });
html.js
をCTRL+X
で保存して終了し、ターミナルに戻ります。
次に、このファイルを2つの新しいファイルにコピーしましょう。 最初のファイルは、HTTP応答でCSVデータを返すことです。
cp html.js csv.js
2番目のファイルは、サーバーでJSON応答を返します。
cp html.js json.js
残りのファイルは、後の演習用になります。
cp html.js htmlFile.js cp html.js routes.js
これで、演習を続行する準備が整いました。 JSONを返すことから始めましょう。
JSONの提供
JavaScript Object Notation は、一般にJSONと呼ばれ、テキストベースのデータ交換形式です。 その名前が示すように、JavaScriptオブジェクトから派生していますが、言語に依存しないため、構文を解析できるすべてのプログラミング言語で使用できます。
JSONは、データを受け入れて返すためにAPIによって一般的に使用されます。 その人気は、XMLなどの以前のデータ交換標準よりもデータ転送サイズが小さいことと、プログラムが過度の労力をかけずにそれらを解析できるようにするツールが存在することによるものです。 JSONについて詳しく知りたい場合は、JavaScriptでJSONを操作する方法に関するガイドをご覧ください。
json.js
ファイルをnano
で開きます。
nano json.js
JSON応答を返したい。 requestListener()
関数を変更して、強調表示された行を次のように変更することにより、すべてのJSON応答が持つ適切なヘッダーを返すようにします。
first-servers / json.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); }; ...
res.setHeader()
メソッドは、応答にHTTPヘッダーを追加します。 HTTPヘッダーは、リクエストまたはレスポンスに添付できる追加情報です。 res.setHeader()
メソッドは、ヘッダーの名前とその値の2つの引数を取ります。
Content-Type
ヘッダーは、要求または応答とともに送信されるデータの形式(メディアタイプとも呼ばれます)を示すために使用されます。 この場合、Content-Type
はapplication/json
です。
それでは、JSONコンテンツをユーザーに返しましょう。 json.js
を次のように変更します:
first-servers / json.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); res.writeHead(200); res.end(`{"message": "This is a JSON response"}`); }; ...
以前と同様に、200
のステータスコードを返すことで、リクエストが成功したことをユーザーに通知します。 今回のresponse.end()
呼び出しでは、文字列引数に有効なJSONが含まれています。
CTRL+X
を押して、json.js
を保存して終了します。 それでは、node
コマンドを使用してサーバーを実行してみましょう。
node json.js
別の端末で、cURLを使用してサーバーにアクセスしましょう。
curl http://localhost:8000
ENTER
を押すと、次の結果が表示されます。
Output{"message": "This is a JSON response"}
これで、アプリを作成する一般的なAPIの多くと同様に、JSON応答が正常に返されました。 標準のターミナルプロンプトに戻ることができるように、必ずCTRL+C
で実行中のサーバーを終了してください。 次に、データを返すもう1つの一般的な形式であるCSVを見てみましょう。
CSVを提供
カンマ区切り値(CSV)ファイル形式は、表形式のデータを提供するために一般的に使用されるテキスト標準です。 ほとんどの場合、各行は改行で区切られ、行の各項目はコンマで区切られます。
ワークスペースで、csv.js
ファイルをテキストエディタで開きます。
nano csv.js
requestListener()
関数に次の行を追加しましょう。
first-servers / csv.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/csv"); res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv"); }; ...
今回は、Content-Type
は、値がtext/csv
であるためCSVファイルが返されていることを示しています。 追加する2番目のヘッダーはContent-Disposition
です。 このヘッダーは、特にブラウザーで、または別のファイルとしてデータを表示する方法をブラウザーに指示します。
CSV応答を返すと、Content-Disposition
ヘッダーが設定されていなくても、ほとんどの最新のブラウザーはファイルを自動的にダウンロードします。 ただし、CSVファイルを返す場合は、CSVファイルの名前を設定できるため、このヘッダーを追加する必要があります。 この場合、このCSVファイルは添付ファイルであり、ダウンロードする必要があることをブラウザに通知します。 次に、ファイルの名前がoceanpals.csv
であることをブラウザに通知します。
HTTP応答にCSVデータを書き込みましょう。
first-servers / csv.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/csv"); res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv"); res.writeHead(200); res.end(`id,name,email\n1,Sammy Shark,[email protected]`); }; ...
前と同じように、応答とともに200
/OK
ステータスを返します。 今回は、res.end()
の呼び出しに、有効なCSVである文字列が含まれています。 カンマは各列の値を区切り、改行文字(\n
)は行を区切ります。 2つの行があります。1つはテーブルヘッダー用で、もう1つはデータ用です。
このサーバーをブラウザーでテストします。 csv.js
を保存し、CTRL+X
でエディターを終了します。
Node.jsコマンドを使用してサーバーを実行します。
node csv.js
別のターミナルで、cURLを使用してサーバーにアクセスしましょう。
curl http://localhost:8000
コンソールには次のように表示されます。
Outputid,name,email 1,Sammy Shark,[email protected]
ブラウザでhttp://localhost:8000
に移動すると、CSVファイルがダウンロードされます。 そのファイル名はoceanpals.csv
になります。
CTRL+C
を使用して実行中のサーバーを終了し、標準のターミナルプロンプトに戻ります。
JSONとCSVを返したので、APIで人気のある2つのケースについて説明しました。 人々がブラウザで表示するWebサイトのデータを返す方法に移りましょう。
HTMLの提供
HTML、ハイパーテキストマークアップ言語は、ユーザーがWebブラウザーを介してサーバーと対話する場合に使用する最も一般的な形式です。 これは、Webコンテンツを構造化するために作成されました。 Webブラウザーは、HTMLコンテンツ、および CSS で追加したスタイルを表示するように構築されています。これは、Webサイトの美しさを変えることができるもう1つのフロントエンドWebテクノロジーです。
テキストエディタでhtml.js
を再度開きましょう。
nano html.js
requestListener()
関数を変更して、HTML応答に適切なContent-Type
ヘッダーを返します。
first-servers / html.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/html"); }; ...
それでは、HTMLコンテンツをユーザーに返しましょう。 強調表示された行をhtml.js
に追加して、次のようにします。
first-servers / html.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(`<html><body><h1>This is HTML</h1></body></html>`); }; ...
まず、HTTPステータスコードを追加します。 次に、有効なHTMLを含む文字列引数を使用してresponse.end()
を呼び出します。 ブラウザでサーバーにアクセスすると、This is HTML
を含む1つのヘッダータグが付いたHTMLページが表示されます。
CTRL+X
を押して保存して終了しましょう。 それでは、node
コマンドを使用してサーバーを実行してみましょう。
node html.js
プログラムが開始されると、Server is running on http://localhost:8000
が表示されます。
次に、ブラウザに移動してhttp://localhost:8000
にアクセスします。 私たちのページは次のようになります。
CTRL+C
で実行中のサーバーを終了し、標準のターミナルプロンプトに戻りましょう。
Node.jsプログラムのように、サーバー側のコードとは別に、HTMLをファイルに書き込むのが一般的です。 次に、ファイルからHTML応答を返す方法を見てみましょう。
ステップ3—ファイルからHTMLページを提供する
Node.jsでHTMLを文字列としてユーザーに提供できますが、HTMLファイルをロードしてそのコンテンツを提供することをお勧めします。 このように、HTMLファイルが大きくなるにつれて、Node.jsコードで長い文字列を維持する必要がなくなり、より簡潔になり、Webサイトの各側面で独立して作業できるようになります。 この「関心の分離」は多くのWeb開発セットアップで一般的であるため、Node.jsでサポートするためにHTMLファイルをロードする方法を知っておくとよいでしょう。
HTMLファイルを提供するために、 fsモジュールを使用してHTMLファイルをロードし、HTTP応答を書き込むときにそのデータを使用します。
まず、Webサーバーが返すHTMLファイルを作成します。 新しいHTMLファイルを作成します。
touch index.html
次に、テキストエディタでindex.html
を開きます。
nano index.html
私たちのウェブページは最小限になります。 背景はオレンジ色で、中央に挨拶のテキストが表示されます。 次のコードをファイルに追加します。
first-servers / index.html
<!DOCTYPE html> <head> <title>My Website</title> <style> *, html { margin: 0; padding: 0; border: 0; } html { width: 100%; height: 100%; } body { width: 100%; height: 100%; position: relative; background-color: rgb(236, 152, 42); } .center { width: 100%; height: 50%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-family: "Trebuchet MS", Helvetica, sans-serif; text-align: center; } h1 { font-size: 144px; } p { font-size: 64px; } </style> </head> <body> <div class="center"> <h1>Hello Again!</h1> <p>This is served from a file</p> </div> </body> </html>
この1つのWebページには、Hello Again!
とThis is served from a file
の2行のテキストが表示されます。 線はページの中央に上下に表示されます。 テキストの最初の行は見出しに表示されます。これは、テキストが大きくなることを意味します。 テキストの2行目は少し小さく表示されます。 すべてのテキストは白で表示され、Webページの背景はオレンジ色になります。
この記事やシリーズの範囲ではありませんが、HTML、CSS、およびその他のフロントエンドWebテクノロジーについて詳しく知りたい場合は、MozillaのWeb入門ガイドを参照してください。 。
HTMLに必要なのはこれだけなので、CTRL+X
を使用してファイルを保存して終了します。 これで、サーバーコードに進むことができます。
この演習では、htmlFile.js
に取り組みます。 テキストエディタで開きます。
nano htmlFile.js
ファイルを読み取る必要があるため、fs
モジュールをインポートすることから始めましょう。
first-servers / htmlFile.js
const http = require("http"); const fs = require('fs').promises; ...
このモジュールには、HTMLファイルを所定の位置にロードするために使用するreadFile()
関数が含まれています。 最新のJavaScriptのベストプラクティスに従って、promiseバリアントをインポートします。 fs
をrequire('fs')
だけに割り当てた場合に使用する必要がある、コールバックよりも構文的に簡潔なpromiseを使用します。 非同期プログラミングのベストプラクティスの詳細については、Node.jsガイドで非同期コードを作成する方法をご覧ください。
ユーザーがシステムを要求したときにHTMLファイルが読み取られるようにします。 requestListener()
を変更して、ファイルを読み取ることから始めましょう。
first-servers / htmlFile.js
... const requestListener = function (req, res) { fs.readFile(__dirname + "/index.html") }; ...
fs.readFile()
メソッドを使用してファイルをロードします。 その引数には__dirname + "/index.html"
があります。 特別な変数__dirname には、Node.jsコードが実行されている場所の絶対パスがあります。 次に、/index.html
を追加して、前に作成したHTMLファイルをロードできるようにします。
次に、ロードされたHTMLページを返します。
first-servers / htmlFile.js
... const requestListener = function (req, res) { fs.readFile(__dirname + "/index.html") .then(contents => { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(contents); }) }; ...
fs.readFile()
promiseが正常に解決されると、データが返されます。 このケースの処理には、then()
メソッドを使用します。 contents
パラメーターには、HTMLファイルのデータが含まれています。
まず、Content-Type
ヘッダーをtext/html
に設定して、HTMLデータを返すことをクライアントに通知します。 次に、リクエストが成功したことを示すステータスコードを記述します。 最後に、ロードしたHTMLページを、contents
変数のデータとともにクライアントに送信します。
fs.readFile()
メソッドは失敗することがあるため、エラーが発生した場合はこのケースを処理する必要があります。 これをrequestListener()
関数に追加します。
first-servers / htmlFile.js
... const requestListener = function (req, res) { fs.readFile(__dirname + "/index.html") .then(contents => { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(contents); }) .catch(err => { res.writeHead(500); res.end(err); return; }); }; ...
ファイルを保存し、CTRL+X
でnano
を終了します。
promiseでエラーが発生すると、それは拒否されます。 その場合は、catch()
メソッドで処理します。 fs.readFile()
が返すエラーを受け入れ、内部エラーが発生したことを示すステータスコードを500
に設定し、エラーをユーザーに返します。
node
コマンドを使用してサーバーを実行します。
node htmlFile.js
Webブラウザーで、http://localhost:8000
にアクセスします。 このページが表示されます:
これで、サーバーからユーザーにHTMLページが返されました。 CTRL+C
で実行中のサーバーを終了できます。 実行すると、ターミナルプロンプトが返されます。
本番環境でこのようなコードを作成する場合、HTTPリクエストを受け取るたびにHTMLページをロードしたくない場合があります。 このHTMLページのサイズは約800バイトですが、より複雑なWebサイトのサイズはメガバイトになる可能性があります。 大きなファイルの読み込みには時間がかかる場合があります。 サイトで大量のトラフィックが予想される場合は、起動時にHTMLファイルをロードし、その内容を保存するのが最適な場合があります。 それらがロードされた後、サーバーをセットアップして、アドレス上の要求をリッスンさせることができます。
この方法を示すために、サーバーをより効率的でスケーラブルに作り直す方法を見てみましょう。
HTMLを効率的に提供する
すべてのリクエストに対してHTMLをロードする代わりに、このステップでは最初に1回ロードします。 リクエストは、起動時にロードしたデータを返します。
ターミナルで、テキストエディタを使用してNode.jsスクリプトを再度開きます。
nano htmlFile.js
requestListener()
関数を作成する前に、新しい変数を追加することから始めましょう。
first-servers / htmlFile.js
... let indexFile; const requestListener = function (req, res) { ...
このプログラムを実行すると、この変数はHTMLファイルの内容を保持します。
それでは、requestListener()
関数を再調整してみましょう。 ファイルをロードする代わりに、indexFile
の内容を返すようになりました。
first-servers / htmlFile.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(indexFile); }; ...
次に、ファイル読み取りロジックをrequestListener()
関数からサーバーの起動にシフトします。 サーバーを作成するときに、次の変更を加えます。
first-servers / htmlFile.js
... const server = http.createServer(requestListener); fs.readFile(__dirname + "/index.html") .then(contents => { indexFile = contents; server.listen(port, host, () => { console.log(`Server is running on http://${host}:${port}`); }); }) .catch(err => { console.error(`Could not read index.html file: ${err}`); process.exit(1); });
ファイルを保存し、CTRL+X
でnano
を終了します。
ファイルを読み取るコードは、最初の試行で記述したものと似ています。 ただし、ファイルの読み取りに成功すると、内容がグローバルindexFile
変数に保存されます。 次に、listen()
メソッドを使用してサーバーを起動します。 重要なのは、サーバーが実行される前にファイルがロードされることです。 このように、indexFile
は空の変数ではなくなったため、requestListener()
関数は必ずHTMLページを返します。
エラーハンドラも変更されました。 ファイルを読み込めない場合は、エラーをキャプチャしてコンソールに出力します。 次に、サーバーを起動せずに、exit()
関数を使用してNode.jsプログラムを終了します。 このようにして、ファイルの読み取りが失敗した理由を確認し、問題に対処してから、サーバーを再起動します。
これで、さまざまなタイプのデータをユーザーに返すさまざまなWebサーバーが作成されました。 これまでのところ、何を返すかを決定するためにリクエストデータを使用していません。 Node.jsサーバーでさまざまなルートまたはパスを設定するときにリクエストデータを使用する必要があるため、次に、それらがどのように連携するかを見てみましょう。
ステップ4—HTTPリクエストオブジェクトを使用したルートの管理
アクセスするほとんどのWebサイトまたは使用するAPIには通常、複数のエンドポイントがあるため、さまざまなリソースにアクセスできます。 良い例は、図書館で使用される可能性のある本の管理システムです。 書籍データを管理するだけでなく、目録作成や検索に便利な著者データも管理する必要があります。
本と著者のデータは関連していますが、それらは2つの異なるオブジェクトです。 このような場合、ソフトウェア開発者は通常、APIユーザーに操作しているデータの種類を示す方法として、さまざまなエンドポイントで各オブジェクトをコーディングします。
2つの異なるタイプのデータを返す小さなライブラリ用の新しいサーバーを作成しましょう。 ユーザーが/books
のサーバーのアドレスにアクセスすると、JSON形式の書籍のリストが表示されます。 /authors
に移動すると、JSONで著者情報のリストを受け取ります。
これまでのところ、受け取ったすべてのリクエストに対して同じ応答を返してきました。 これを簡単に説明しましょう。
JSON応答の例を再実行します。
node json.js
別の端末で、前のようにcURLリクエストを実行しましょう。
curl http://localhost:8000
次のように表示されます。
Output{"message": "This is a JSON response"}
次に、別のcurlコマンドを試してみましょう。
curl http://localhost:8000/todos
Enter
を押すと、同じ結果が表示されます。
Output{"message": "This is a JSON response"}
requestListener()
関数には、URLに/todos
が含まれるリクエストを処理するための特別なロジックが組み込まれていないため、Node.jsはデフォルトで同じJSONメッセージを返します。
ミニチュアライブラリ管理サーバーを構築したいので、ユーザーがアクセスするエンドポイントに基づいて、返されるデータの種類を分離します。
まず、CTRL+C
で実行中のサーバーを終了します。
次に、テキストエディタでroutes.js
を開きます。
nano routes.js
requestListener()
関数の前に、JSONデータを変数に格納することから始めましょう。
first-servers / routers.js
... const books = JSON.stringify([ { title: "The Alchemist", author: "Paulo Coelho", year: 1988 }, { title: "The Prophet", author: "Kahlil Gibran", year: 1923 } ]); const authors = JSON.stringify([ { name: "Paulo Coelho", countryOfBirth: "Brazil", yearOfBirth: 1947 }, { name: "Kahlil Gibran", countryOfBirth: "Lebanon", yearOfBirth: 1883 } ]); ...
books
変数は、ブックオブジェクトの配列のJSONを含む文字列です。 各本には、タイトルまたは名前、著者、および出版された年があります。
authors
変数は、作成者オブジェクトの配列のJSONを含む文字列です。 各著者には、名前、出生国、および出生年があります。
応答が返すデータができたので、requestListener()
関数を変更して正しいルートに返すようにしましょう。
まず、サーバーからのすべての応答に正しいContent-Type
ヘッダーがあることを確認します。
first-servers / routers.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); } ...
ここで、ユーザーがアクセスするURLパスに応じて適切なJSONを返します。 リクエストのURLにswitchステートメントを作成しましょう。
first-servers / routers.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) {} } ...
リクエストオブジェクトからURLパスを取得するには、そのurl
プロパティにアクセスする必要があります。 switch
ステートメントにケースを追加して、適切なJSONを返すことができるようになりました。
JavaScriptのswitch
ステートメントは、オブジェクトまたはJavaScript式の値(たとえば、数学演算の結果)に応じて、実行されるコードを制御する方法を提供します。 それらの使用方法に関するレッスンやリマインダーが必要な場合は、JavaScriptでSwitchステートメントを使用する方法に関するガイドをご覧ください。
ユーザーが書籍のリストを取得したい場合に備えて、case
を追加して続けましょう。
first-servers / routers.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) { case "/books": res.writeHead(200); res.end(books); break } } ...
ステータスコードを200
に設定して、リクエストが正常であることを示し、書籍のリストを含むJSONを返します。 次に、作成者用に別のcase
を追加しましょう。
first-servers / routers.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) { case "/books": res.writeHead(200); res.end(books); break case "/authors": res.writeHead(200); res.end(authors); break } } ...
以前と同様に、リクエストは正常であるため、ステータスコードは200
になります。 今回は、作成者のリストを含むJSONを返します。
ユーザーが他のパスに移動しようとすると、エラーを返します。 これを行うためにデフォルトのケースを追加しましょう:
ルート.js
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) { case "/books": res.writeHead(200); res.end(books); break case "/authors": res.writeHead(200); res.end(authors); break default: res.writeHead(404); res.end(JSON.stringify({error:"Resource not found"})); } } ...
switch
ステートメントでdefault
キーワードを使用して、以前のケースではキャプチャされなかった他のすべてのシナリオをキャプチャします。 ステータスコードを404
に設定して、探していたURLが見つからなかったことを示します。 次に、エラーメッセージを含むJSONオブジェクトを設定します。
サーバーをテストして、期待どおりに動作するかどうかを確認しましょう。 別の端末で、最初にコマンドを実行して、本のリストが返されるかどうかを確認しましょう。
curl http://localhost:8000/books
Enter
を押すと、次の出力が表示されます。
Output[{"title":"The Alchemist","author":"Paulo Coelho","year":1988},{"title":"The Prophet","author":"Kahlil Gibran","year":1923}]
ここまでは順調ですね。 /authors
についても同じことを試してみましょう。 ターミナルで次のコマンドを入力します。
curl http://localhost:8000/authors
コマンドが完了すると、次の出力が表示されます。
Output[{"name":"Paulo Coelho","countryOfBirth":"Brazil","yearOfBirth":1947},{"name":"Kahlil Gibran","countryOfBirth":"Lebanon","yearOfBirth":1883}]
最後に、誤ったURLを試して、requestListener()
がエラー応答を返すことを確認しましょう。
curl http://localhost:8000/notreal
そのコマンドを入力すると、次のメッセージが表示されます。
Output{"error":"Resource not found"}
CTRL+C
で実行中のサーバーを終了できます。
これで、ユーザーがさまざまなデータを取得するためのさまざまな手段が作成されました。 また、サポートされていないURLをユーザーが入力した場合にHTTPエラーを返すデフォルトの応答を追加しました。
結論
このチュートリアルでは、一連のNode.jsHTTPサーバーを作成しました。 最初に、基本的なテキスト応答を返しました。 次に、サーバーからさまざまな種類のデータ(JSON、CSV、HTML)を返しました。 そこから、ファイルの読み込みとHTTP応答を組み合わせて、サーバーからユーザーにHTMLページを返し、ユーザーの要求に関する情報を使用して応答で送信するデータを決定するAPIを作成することができました。
これで、さまざまな要求と応答を処理できるWebサーバーを作成する準備が整いました。 この知識があれば、さまざまなエンドポイントのユーザーに多くのHTMLページを返すサーバーを作成できます。 独自のAPIを作成することもできます。
Node.jsのその他のHTTPWebサーバーについて詳しくは、http
モジュールのNode.jsドキュメントをご覧ください。 Node.jsの学習を続けたい場合は、Node.jsシリーズのコーディング方法ページに戻ることができます。