Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expressの章を改定 #365

Merged
merged 2 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
94 changes: 84 additions & 10 deletions docs/3-web-servers/05-template-engine/index.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
---
title: Express とテンプレートエンジン
title: Express によるサーバー構築
---

import CodeBlock from '@theme/CodeBlock';
import Term from "@site/src/components/Term";
import ViewSource from "@site/src/components/ViewSource";

## Express パッケージを用いて HTTP サーバーを構築する
## ウェブサイトが動作する仕組み

[Express パッケージ](https://www.npmjs.com/package/express) を用いると、Node.js 標準の `http` モジュールよりも簡単に Web サーバーを構築できます。
[「Web プログラミングの基礎を学ぼう」](../../1-trial-session/index.md) の章では、ウェブサイトを表示するために HTML ファイルと JavaScript ファイルを作成し、ブラウザから開きました。しかしながら、通常のウェブサイトではこのような手順は踏まず、URL をブラウザに入力することにより閲覧することができます。

Web では、通常インターネットを介してデータをやり取りします。インターネットを人間が直接利用することはできないので、何らかのコンピューターを使用しなければなりません。このとき、通常は
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved

- **クライアント**: サービスを利用する側のコンピューターや、その上で直接通信を担うソフトウェア
- **サーバー**: サービスを提供する側のコンピューターや、その上で直接通信を担うソフトウェア

という二者の関係が発生します。また、その間で発生する通信を、その方向により

- **リクエスト**: クライアントからサーバーに対する要求
- **レスポンス**: リクエストに対する応答
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved

のように区別して呼びます。それでは、Node.js で Web サーバーを作ってみましょう。

## Express パッケージを用いて Web サーバーを構築する

[Express パッケージ](https://www.npmjs.com/package/express) を用いると、簡単に Web サーバーを構築できます。

まずは `express` パッケージを npm でインストールします。

Expand All @@ -23,18 +39,72 @@ const express = require("express");
const app = express();

app.get("/", (request, response) => {
response.send("Hello Express");
response.send("Hello World");
});

app.listen(3000);
```

`main.js` を起動することにより、 `http://localhost:3000/` で `Hello Express` が表示されます。
ファイルを保存したら、作成したファイルを実行し、ブラウザで `http://localhost:3000/` にアクセスしてみましょう。ブラウザに `Hello World` と表示されましたか?
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved

![HTTPサーバー](./http-server.png)

:::caution Web サーバーの停止

このプログラムは、一度起動すると停止しません。サーバーにとって、クライアントからのリクエストはいつやってくるかわからないため、常に起動し続けている必要があるからです。Node.js プログラムを終了するには、ターミナル上で `Ctrl + C` を押します。
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved

:::

書いたコードを詳しく見てみましょう。

まず、`require("express")` の戻り値は関数となっており、この関数を呼び出すことにより、[`express.Application`](https://expressjs.com/ja/api.html#app) クラスのインスタンスが作成されます。

[`express.Application#get` メソッド](https://expressjs.com/ja/api.html#app.get.method)は、クライアントから特定のパスに対してリクエストが来た時に実行される関数を追加するメソッドです。第 1 引数にはパスの文字列を、第 2 引数には実行される関数を指定します。

例えば今回であれば第 1 引数に `"/path"` を渡すと `http://localhost:3000/path` にリクエストが来たときに関数が実行されることになります。

第 2 引数の関数を詳しく見てみましょう。この関数は2つの引数をとります。具体的には第 1 引数に受け取ったリクエストを表す [`express.Request` クラス](https://expressjs.com/ja/api.html#req) のインスタンスが、第 2 引数にこれから送るレスポンスを表す [`express.Response` クラス](https://expressjs.com/ja/api.html#res) のインスタンスが渡されます。
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved

そして [`express.Response#send` メソッド](https://expressjs.com/ja/api.html#res.send)により、クライアントが必要なデータを送信することができます。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

「クライアントが必要なデータを送信することができます。」のところは、主語がクライアントとサーバーの2通りで捉えられて混乱をきたしそう。


:::tip `http`標準<Term type="javascriptModule">モジュール</Term>
`express` を使わずに Node.js 単体 で Web サーバーを作成するには、`http` 標準<Term type="javascriptModule">モジュール</Term>を使用します。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NITS]
express=>Expressで良さげ。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NITS]
「単体」と「で」の間に、スペースが入ってる。


`http` 標準モジュールを使って 簡単な Web サーバーを構築すると以下のようなコードになります。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NITS]
不要なスペース


```javascript title=main.js
const http = require("http");

const server = new http.Server();

server.addListener("request", (request, response) => {
response.write("Hello World");
response.end();
});

server.listen(3000);
```

[`http.Server` クラス](https://nodejs.org/api/http.html#class-httpserver) は、サーバーを作成するためのクラスです。このクラスの [`addListener` メソッド](https://nodejs.org/api/events.html#emitteraddlistenereventname-listener) は、イベントハンドラを追加するためのメソッドです。第 1 引数にイベントの名前、第 2 引数にイベントハンドラとなる関数オブジェクトを指定します。

[`request` イベント](https://nodejs.org/api/http.html#event-request) は、クライアントからリクエストが来るたびに発生するイベントです。イベントハンドラの第 1 引数に受け取ったリクエストを表す [`http.IncomingMessage` クラス](https://nodejs.org/api/http.html#class-httpincomingmessage) のインスタンスが、第 2 引数にこれから送るレスポンスを表す [`http.ServerResponse` クラス](https://nodejs.org/api/http.html#class-httpserverresponse) のインスタンスが渡されます。

[前頁](../04-http-server/index.md) の `http` 標準モジュールを用いた例とほとんど同じことを行うプログラムになっていますが、`express` を使う場合は少々異なる部分があります。まず、`require("express")` の戻り値は関数となっており、この関数を呼び出すことにより、[`express.Application`](https://expressjs.com/ja/api.html#app) クラスのインスタンスが作成されます
`express` パッケージと比較してみましょう
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここも合わせたら、Expressでいいんじゃない?


[`express.Application#get` メソッド](https://expressjs.com/ja/api.html#app.get.method)は、`http` 標準モジュールにおける `request` イベントハンドラの登録に相当する操作を行うためのメソッドです。イベントハンドラの引数に `request` と `response` が存在する点では一致していますが、Express では [`express.Response#send` メソッド](https://expressjs.com/ja/api.html#res.send)が利用できます。これは、[`http.ServerResponse#write`](https://nodejs.org/api/http.html#responsewritechunk-encoding-callback) メソッドと、[`http.ServerResponse#end`](https://nodejs.org/api/http.html#responseenddata-encoding-callback) メソッドを順番に呼ぶ操作に対応します。

:::

## HTTP

インターネット上には、さまざまなデータが流れています。インターネットに接続しているコンピューターが好き勝手にデータを送受信しても、意味のあるやり取りは成立しません。このため、通信を行うための手順を標準化しておく必要があります。こうしてできた手順のことを、**プロトコル**と呼びます。

Web の世界で用いられるプロトコルは、通常 **HTTP** と呼ばれるものです。ブラウザに `http://example.com/path/to/index.html` が入力された場合、ブラウザとサーバーの間で次の図のような通信が行われます。

![HTTP](./basic-http.png)

Web サーバーにアクセスするために用いた http://localhost:3000/ のうち、http はプロトコルを、localhost:3000 はサーバーの所在地を表しています (localhost は自分のコンピューターを指します)。
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved

## 静的ホスティング

次の例では、`/`、`/script.js`、`/sub/`、`/sub/script.js` へのリクエストについて、それぞれファイルから読み込んでレスポンスを送信しています。
Expand Down Expand Up @@ -82,7 +152,7 @@ app.listen(3000);

:::

## EJS テンプレートエンジン
## 動的なウェブページ
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved

前項のプログラムを書き換えて、複雑な HTML を出力できるようにしてみましょう。

Expand Down Expand Up @@ -121,9 +191,11 @@ console.log(["Apple", "Banana", "Orange"].join("/")); // Apple/Banana/Orange

:::

なかなか大変なことになっています。これから HTML がもっと長くなったり、さらに複雑なプログラムが必要になってきたらこのまま続けていくのは難しそうです
このようにテンプレートリテラルを用いることで、JavaScript のプログラムから HTML に変更を加え出力することができます
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved

[EJS](https://ejs.co/) をはじめとした**テンプレートエンジン**は、プログラミング言語から HTML などを作成する作業を簡単にしてくれます。`ejs` パッケージを npm でインストールしてください。先ほどのプログラムを、EJS を用いて書き換えると、次のようになります。
:::tip テンプレートエンジン
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved
上記のようにテンプレートリテラルを使って HTML を生成することもできますが、HTML がもっと長くなったり、さらに複雑なプログラムが必要になってきたらこのまま続けていくのは難しそうです。
[EJS](https://ejs.co/) をはじめとした**テンプレートエンジン**は、プログラミング言語から HTML などを作成する作業を簡単にしてくれます。先ほどのプログラムを、EJS を用いて書き換えると、次のようになります。(手元で試したい場合は ejs をインストールしてください)
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved

```javascript title=main.js
const fs = require("fs");
Expand Down Expand Up @@ -168,10 +240,12 @@ app.listen(3000);

テンプレート内の `<%` から `%>` で囲まれた部分は、JavaScript のプログラムとして実行されます。また、`<%=` から `%>` で囲まれた部分は JavaScript の式として評価され、最終的な結果に埋め込まれます。

:::

## 課題

- Express を用いて、`あなたは n 人目のお客様です。` とレスポンスする Web サーバーを作成してください。`n` はアクセスされるたびに 1 ずつ増えるようにしてください。
- 上記プログラムに EJS を追加してみてください。
- (発展) 上記プログラムに EJS を追加してみてください。
kazukinumazato marked this conversation as resolved.
Show resolved Hide resolved
- (重要) アクセスされた時刻をウェブサーバー側で求めて表示するウェブサーバーと、ブラウザに求めさせるウェブサーバーをそれぞれ作成してください。
- この 2 つの違いは何でしょうか。どういった場合にどちらの手法を使うのが適切でしょうか。

Expand Down