Lesson 4

データのやり取り

Lesson 4 Chapter 1
リクエストデータを分析する

今回のLessonでは、クライアントからリクエストデータを受け取る方法を学びます。Expressの基本的なメソッドを使って、必要な情報を取得しましょう。

サンプルプロジェクトの準備

まずは任意のディレクトリにプロジェクトを作成します。コマンドラインから作成する場合は、以下の例を参考にしてください。

ターミナル(Windows)
$ mkdir sample-app2
$ cd sample-app2
$ npm init -y
$ npm install express ejs
$ New-item app.js

Hello world

それではHello worldをブラウザに出力しましょう。

app.js
const express = require('express')
const app = express()
const PORT = 3000

app.get('/', (req, res) => {
  res.send('Hello world')
})

app.listen(PORT, () => {
  console.log(`Starting server on PORT: ${PORT}`)
})

作成したファイルをNode.jsで実行します。以下のコマンドを入力してください。

ターミナル
node app.js

任意のブラウザで「http://localhost:3000/」にアクセスします。画像のように「Hello world」の文字列が表示されたら準備完了です。

browser_ch1-1.png Hello world

リクエストオブジェクトの取得

それでは、クライアントからのリクエストデータを取得しましょう。といっても、取得するためのメソッドはすでに登場しています。以下のコードはLesson3-3「サンプルページを実装」で例示したものです。

app.js
const express = require('express')
const app = express()

app.get('/', (req, res, next) => {
  res.send('hello express')
  })
)

app.getは、第1引数のエンドポイントにGETリクエストが呼ばれたとき、第2引数のコールバック関数を起動します。

エンドポイントとは

Lesson3-6「ルーティングの実装」で解説した通り、「エンドポイント」とは「http://localhost:3000/~」における~の部分です。

コールバック関数の引数に注目してください。以下の通り、reqresという2つの引数を取ります。

  • req:クライアントからのリクエスト情報を格納したオブジェクト
  • res:レスポンスの情報を格納したオブジェクト

今回はリクエストデータを取得するため、reqの中身を分析していきます。

Expressの基本的なメソッド として、req.queryreq.paramsreq.pathreq.urlの4つを見ていきましょう。

req.query:クエリパラメーターを取得

クエリパラメーターの取得には、req.queryメソッドを使用します。

クエリパラメーターとはURLの?の後に続く値のことです。たとえば、検索文字列など動的な値を送信する際に用いられます。

クエリオブジェクトの取得

先ほど作成した「app.js」に、以下のコードを追記してください。

app.js
app.get('/todo', (req, res) => {
  res.send(req.query)
})

http://localhost:3000/todo?todo=おにぎりを買う」にブラウザでアクセスしてみましょう。画像のように表示されたでしょうか?

クエリ情報はオブジェクトで格納されていることがわかります。

query_ch1-1.png クエリオブジェクトの取得(レイアウトは異なる場合があります)

復習:オブジェクトとは

オブジェクトとは、データを階層的に表現し、まとまりとして参照できるようにしたデータ構造のことです。プロパティ(keyvalue)のペアによって構成されます。

オブジェクトの一例
const user = {
  name: 'taro',
  age: 18
}
たとえば上記の場合、 name, agekeytaro, 18valueにあたります。

クエリ文字列の取得

それでは、クエリに含まれる情報の中から、文字列だけを抽出してみましょう。

先ほどの「app.js」を以下の通り修正します。

app.js
app.get('/todo', (req, res) => {
  res.send(req.query.todo)
})

req.query.todoとすることで、クエリ内のtodoプロパティを直接参照しています。サーバーを起動すると、以下のようにページに表示されるはずです。

query_ch1-2.png クエリ文字列の取得(レイアウトは異なる場合があります)

今度はオブジェクトではなく、プロパティの文字列だけを取得できました。以上がreq.queryメソッドの基本的な使い方です。

req.params:パスパラメーターを取得

req.paramsメソッドは、パスパラーメーターを取得する目的で使用されます。

パスパラメーターを使うことで動的なルートティングが可能となり、複雑な構成のWebページを作成できます。

動的ルートマッチング

ルーティングの基本について復習したい方は、Lesson3-6「ルーティングの実装」を読み直してください。

パラメーターオブジェクトの取得

それでは実践してみましょう。「app.js」に以下のコードを追記します。

app.js
app.get('/todo/:id', (req, res) => {
  res.send(req.params)
})

サーバーをnode app.jsで起動した後、ブラウザで「http://localhost:3000/todo/100」にアクセスしてみましょう。画像のように表示されたでしょうか?

req.queryの場合と同様、オブジェクトとして取得できていることがわかります。

params_ch1-1.png パラメーターオブジェクトの取得(レイアウトは異なる場合があります)

パラメーターの値の取得

プロパティの参照方法もrea.queryと同じです。以下のようにコードを書き換えてみましょう。

app.get('/todo/:id', (req, res) => {
  res.send(req.params.id)
})

サーバーを再起動して「http://localhost:3000/todo/100」にもう一度アクセスします。

params_ch1-2.png パラメーターオブジェクトの取得

パラーメーター100を正しく取得できていることが分かります。

このように、動的なパラメーターは「:」を前につけて定義できます。上記の例では:idとしていますが、他の定義しても構いません。たとえば、以下はidtitleに変更したコードです。

気になる人はブラウザで動作確認してみましょう。

app.get('/todo/:title', (req, res) => {
  res.send(req.params.title)
})

req.path:エンドポイント+パスパラメーターを取得

req.pathメソッドは、エンドポイントとパスパラーメーターを合わせて取得できます。

使い方はreq.queryreq.paramsと同じです。以下の通りコードを書き換え、サーバーを再起動しましょう。

app.get('/todo/:id', (req, res) => {
  res.send(req.path)
})

path_ch1-1.png パスの取得

エンドポイント/todo/、パスパラメーター100が正しく取り出されています。

req.url:エンドポイント+パスパラメーター+クエリパラメーターを取得

req.urlメソッドでは、エンドポイントとパスパラーメーターとクエリパラメーターを合わせたものが取得できます。

こちらも他のメソッドと同じ使い方です。以下の通り記述してみましょう。

app.js
app.get('/todo/:id', (req, res) => {
  res.send(req.url)
})

コードを書き換えたらサーバーを再起動し、「http://localhost:3000/todo/100?title=HelloWorld 」にアクセスしてください。

/url_ch1-1.png URLの取得

エンドポイント/todo/、パスパラメーター100、クエリパラメーターtitle=HelloWorldが正しく取り出されています。

まとめ

リクエストオブジェクトを用いると、クエリパラメータなどのURLに含まれる情報を簡単に取得できます。今回は基本となる4つのメソッドを紹介しました。

メソッド名 説明
req.query クエリパラメーターを取得する
req.params パスパラメーターを取得する
req.path エンドポイント+パスパラメーターを取得する
req.url エンドポイント+パスパラメーター+クエリパラメーターを取得する

Lesson5で取り上げますが、他に「リクエストボディ」を取得するメソッドも存在します。

リクエストデータを扱うことは、Expressでアプリケーションを開発する上で必須の知識です。しっかりと身につけましょう。

Lesson 4 Chapter 2
リクエストデータを処理する

前回のChapter1では、クライアントからリクエストデータを受け取る方法について学びました。Expressのreq.queryなどのメソッドを使用すると、リクエストデータの中から必要な情報だけを簡単に取得できます。

今回のChapter2では、データ取得後の処理についても触れなければなりません。というのも、実際のアプリケーションではエラーチェックや認証、ロギングなど、実に様々な機能が必要となるからです。

Expressには、こうした共通処理を円滑に行うために「ミドルウェア」の仕組みが実装されています。今回はこのミドルウェアを中心に、リクエストデータの適切な扱いについて学びましょう。

共通処理の必要性

Expressの機能は必要最低限です。HTTPリクエストを受け、それに対応するレスポンスを返します。Chapter1では、その最もシンプルな例を示しました。

app.js
app.get('/', (req, res) => {
  res.send('Hello world')
})

しかし実際には、このリクエストとレスポンスの間に様々な処理が挟まれます。

ひとつの例として、リクエストごとにセッション情報を判定する処理を考えてみましょう。もしセッションの有効性が切れていた場合、直ちにユーザーをログイン画面へと遷移させなければなりません。

いくらエンドポイントごとに判定が必要だからといっても、すべてのルーティングで同じ処理を記述したのでは大変です。

この場合、セッション情報の判定は共通処理として実装するべきでしょう。Expressのミドルウェアを用いることで、上記の問題は簡単に解決できます。

ミドルウェアとは

ミドルウェアは、リクエストとレスポンスの間に挟まる共通処理です。元々はOSとアプリケーションの中間層を指す言葉でしたが、現在はOSに限らず、Webフレームワークにおける共通処理を指すようになりました。

Expressにおける「ミドルウェア」も、この用法に従ったものです。ミドルウェアと聞くと難しく聞こえるかもしれませんが、実態は単なる関数呼び出しに過ぎません。

クライアントからリクエストを受信すると、Expressアプリケーションは所定のミドルウェア関数を通過し、最終的なレスポンスを返します。

ミドルウェアを記述する

それでは実際にミドルウェアを記述していきましょう。前回作成したプロジェクトを引き続き使用します。「app.js」ファイルを以下のように書き換えてください。

app.js
const express = require('express')
const app = express()
const PORT = 3000

app.use((req, res, next) => {
  console.log('全共通ミドルウェア')
  next()
})

app.get('/todo', (req, res) => {
  res.send('hello todo')
})

app.get('/user', (req, res) => {
  res.send('hello user')
})

app.listen(PORT, () => {
  console.log(`Starting server on PORT: ${PORT}`)
})

サーバーを「node app.js」で再起動した後、以下にアクセスしてみましょう。

画像のようにコンソールに表示されたら、ミドルウェアの呼び出しは成功です。

middleware_console-ch2-1.png コンソール

それでは、複数回画面を更新してみましょう。画面を更新した数だけコンソールに出力が追加されるはずです。このように、ミドルウェアを使えばリクエストの度に共通処理を呼び出せます。

ミドルウェアの仕組み

先のコードに書かれたapp.useがミドルウェアの記述にあたる箇所です。あらためて抜き出してみましょう。

app.js
app.use('/todo', (req, res, next) => {  // todoにアクセス
  console.log(`リクエストエンドポイント: ${req.url}`)
  next()
})

ここでのポイントは以下の2つです。

  • '/todo'app.useの第1引数にエンドポイントを指定すると、特定のリクエストのみ処理を適用できます。
  • next():ミドルウェアから次の処理に移行するため、必ずnextを呼ばなければなりません。

ミドルウェアの特徴として、複数の処理を続けて呼ぶことが可能です。一連の処理を呼び出したい場合、以下のように続けて記述します。

app.use((req, res, next) => {
  console.log(`全共通ミドルウェア`)
  next()
})
app.use('/user', (req, res, next) => {
  console.log(`userミドルウェア`)
  next()
})

上記の例で/userにリクエストを送ると、最初に共通のミドルウェアが呼び出され、続けて指定エンドポイントのミドルウェアが呼び出されます。

リクエストデータを処理する

それではリクエストデータに共通の処理を挟み込んでみましょう。コードを少し書き換えます。

app.js
app.use((req, res, next) => {
  // 変更
  console.log(`リクエストエンドポイント: ${req.url}`)
  next()
})

app.get('/todo', (req, res) => {
  res.send('hello todo')
})

// 追加
app.get('/todo/:id', (req, res) => {
  res.send(`hello todo ${req.params.id}`)
})

書き換えたらサーバーを再起動して、以下のアドレスにアクセスしてみましょう。

画像の通り、対応するエンドポイントが表示されていれば成功です。

例示したように、エンドポイントのログを出力することは、ミドルウェアの効果的な使用法のひとつです。こうした「ロギング」の知見については、Lesson7で詳しく取り上げます。

middleware_console-ch2-2.png 共通ログ

まとめ

今回のChapter2では、リクエストデータを処理するため、Expressのミドルウェアについて学びました。繰り返しになりますが、ミドルウェアはリクエストとレスポンスの間に挟まれる共通処理です。

今回の例では、受信したエンドポイントを単にログ出力するだけでした。実際には、クッキーの取得やログイン認証など、様々な処理がミドルウェアで行えます。また、公開されている外部のライブラリを使用することで、より自由度の高い構築も可能です。

Lesson 4 Chapter 3
レスポンスを返却する

本章のChapter1ではExpressを用いたリクエストデータの取得を、Chapter2では取得したデータの処理を学びました。Chapter3では、Expressでレスポンスを返却する方法について学びます。

といっても、すでに私たちはres.sendメソッドを使って簡単なレスポンスを返しています。今回はres.renderを使って、画面に動的な値を描画することにしましょう。

Expressがレスポンスを返すためには、何よりまずクライアントからのリクエストを取得しなればなりません。また、Expressが返した値をEJS側で表示する処理も必要です。

アプリケーションの中で自分が何の処理を行っているのか、随時確認しながら学習を進めてください。

プロジェクトの準備

最初に、Express+EJSでページを表示するための準備を行います。

プロジェクトの準備

前回のプロジェクトを使用します。プロジェクトの準備ができていない方は、本章のChapter1「リクエストデータを分析する」を参考に進めてください。

プロジェクトの直下に「views」ディレクトリを、「views」ディレクトリの配下に「index.ejs」ファイルを作成してください。

コマンドライン上からは、以下の入力で作成できます。

~/workplace/sample-app2 (Mac / Linux)
$ mkdir views
$ touch views/index.ejs
~\workplace\sample-app2 (Windows)
$ mkdir views
$ New-Item views/index.ejs
                    

念のため、以下の画像と同じディレクトリ構成であることを確認してください。

dir-ch3-1.png 現在のディレクトリ構成

Expressでリクエストを受け取る

新規作成した「index.ejs」を編集し、以下のコードを記述してください。

index.ejs
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello world</h1>
  </body>
</html>

上記では、確認用としてHTMLによる静的なページを表示させています。最終的には、Hello worldの部分にExpressから受け取った値を表示する流れです。

次に「app.js」を編集し、以下の通り記述してください。

app.js
const express = require('express')
const path = require('path')
const app = express()
const PORT = 3000

// EJS用の設定追加
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')

// ミドルウェア
app.use((req, res, next) => {
  console.log(`リクエストエンドポイント: ${req.url}`)
  next()
})

// /todoにアクセスが来たらindex.ejsを返す
app.get('/todo', (req, res) => {
  res.render('index.ejs')
})

app.get('/todo/:id', (req, res) => {
  res.send(`hello todo ${req.params.id}`)
})

app.listen(PORT, () => {
  console.log(`Starting server on PORT: ${PORT}`)
})

ここではExpressのres.renderメソッドを利用し、「index.ejs」の内容をWebページとして描画しています。

以上で準備が整いました。コマンドラインからnode app.jsと入力してサーバーを起動し、Webブラウザで「http://localhost:3000/todo」にアクセスしましょう。

ページ上に「Hello world」と表示されればテスト成功です。

Expressでレスポンスを返却する

リクエストは正常に受信できましたが、ここからが本題です。今度は指定した値を返却し、先ほどの「index.ejs」に表示させてみます。

「app.js」ファイルを以下のように変更してください。

app.js
...省略

// /todoにアクセスが来たらindex.ejsを返す
app.get('/todo', (req, res) => {
  res.render('index.ejs', { username: 'John' })
})

...省略

上記の通り、Expressのres.renderメソッドは第2引数を介してデータを返却できます。上記の例では、{ username: 'Jhon' }の箇所です。必ずオブジェクト形式で渡す点に注意してください。

EJSでレスポンスの値を表示する

最後に、「index.ejs」でレスポンスの値を受け取るコードを書いていきましょう。usernameの部分に「app.js」で書いたres.renderの第2引数が入ります。

EJSの記法についてはLesson3で学習した通りです。ここでは、タグを用いて変数usernameの値を表示しています。

index.ejs
<body>
    <h1>Hello </h1>
</body>

これでレスポンスの値が表示できるようになりました。コマンドラインからnode app.jsを入力し、サーバーを起動してください。

ブラウザから「https://localhost:3000/todos/」にアクセスします。

chrome_01.png 「Hello John」と表示されます

ページ上に「Hello John」と表示されたら成功です。

まとめ

今回はEJSでExpressからのレスポンスを返す方法を学びました。

上記の例では単純な文字列を返しましたが、もちろんオブジェクトや配列、数字や真偽値なども返却可能です。

ここまでExpressによる送受信を学びましたが、思ったよりも簡単にリクエスト/レスポンスの処理が行えたのではないでしょうか?

Expressを使えば、このように少ない記述量でWebアプリケーションが開発できます。次のChapter4では、Tipsとしてセッション管理の手法を学びましょう。

Lesson 4 Chapter 4
セッションの取り扱い

Lesson4では、Expressを用いたデータの送受信を学んできました。実践を通して、HTTPリクエスト・レスポンスに対する理解が一段と深まったことでしょう。

次のLesson5では、データベースと連携したWebアプリケーションを作成します。その前の箸休めとして、文中で触れられなかった「セッション」と「Cookie」の知識を補うことにしましょう。

セッションもCookieも、いまや広く普及している仕組みです。当然Webアプリケーションとも密接に関わってくるため、十分な理解に努めてください。

セッションとは

端的に説明すると、セッションは「サーバーがクライアントごとの情報を保管するための仕組み」です。

具体例として、AmazonのようなECサイトを想像してみましょう。ECサイトで商品を選択すると、その情報がショッピングカートに保存されます。たとえ他の商品ページに移動したとしても、一度選択した商品はカート内に残り続け、購入数や合計金額がリセットされることはありません。

これまで解説してきた単純なデータのやり取りでは、ECサイトのような仕組みを構築することは難しいでしょう。というのも、たとえ商品の情報をリクエストとして受け取ったとしても、それがどのユーザーに由来するのか、サーバー側では個別に判断できないからです。

IPアドレスによる識別

「IPアドレスを使えばユーザーを識別できる」と思われるかもしれません。しかし、IPアドレスはプロバイダーなどによって動的に変更される可能性があり、ユーザーを区別するには不十分です。

そこで考案されたのが、「Cookie」によるセッション管理の手法です。基本的な流れを以下に示します。

  1. セッションの開始時、WebサーバーはWebブラウザに対してCookieと呼ばれる一意の情報を送る。
  2. ブラウザ側はこのCookieを保存し、以降リクエストを送る度にHTTPヘッダーに付与する。
  3. サーバー側は送られてくるCookieを参照し、ユーザーを識別する。

上記の通り、セッションの手法はCookieと強く結びついています。以下の項目では、このCookieについて詳しく学びましょう。

Cookieとは

Cookieは、クライアントの状態を文字列で保持する仕組みです。前述したセッションのように、クライアント-サーバー間で状態を維持したいときに使用します。

Cookieを用いることで、たとえば以下の仕組みが実現可能です。

  • ECサイトでショッピングカートの情報を保持する
  • SNSなどの会員制サービスでログイン情報の入力を省く
  • サイト訪問者ののアクセス情報を解析する
  • サイト訪問者の閲覧履歴をもとにターゲティング広告を出す

Cookieはブラウザの開発者ツールでも確認できます。実際に書いて確かめてみましょう。

cookie-parser

ExpressでCookieを利用するためにはcookie-parserというモジュールが必要です。cookie-parserを使うと、クライアントから送信されたCookieをサーバー側で参照できるようになります。

さっそくnpmを使ってインストールしましょう。

~/sample-app2
npm install cookie-parser

インストールが完了したら、ミドルウェアで読み込みます。クライアントのCookieに対して適当な文字列をセットし、送信されたCookieをサーバーから参照してください。

app.js
const express = require('express')
const path = require('path')
// 追加
const cookieParser = require('cookie-parser')

...

// ミドルウェア
app.use((req, res, next) => {
  console.log(`リクエストエンドポイント: ${req.url}`)
  next()
})
// 追加
app.use(cookieParser())

// 追加
app.use((req, res, next) => {
  console.log(req.cookies)
  res.cookie('test', 'Hello')
  next()
})

ミドルウェアとして定義したcookieParserを使って、クライアントからのCookieを参照しています。上記の例ではtestというキーに対して、文字列「Hello」をCookieとしてクライアントへ渡しています。

Cookieが送信される仕組み

Cookieの送信はブラウザ側で実装されている機能であり、ソースコード中に特別な記述をする必要はありません。

この状態で「http://localhost:3000/todo」へアクセスしましょう。画像のようにコンソールに表示されるでしょうか?

terminal_ch4-1.png Cookie出力

{ test: Hello }と表示されていれば、ブラウザ側で保存されたCookieが正しく送信されていることになります。

開発者ツールから確認する

あわせて、開発者ツールからブラウザにCookieが設定されているか確認しましょう。Google Chromeの場合、以下の手順で確認できます。

  1. 開発者ツールを開く(F12、もしくは右クリックで「検証」を押下する)
  2. 「Application」タブを開く
  3. 「Storage」の中の「Cookies」を開く

developper-tool.png 開発者ツール確認

Cookieにセットした値が確認できるはずです。Nameに設定した「test」をキーとしてCookieの値である「Hello」が取得できます。

まとめ

今回はセッションとCookieに関する知識を学びました。ポイントは以下の通りです。

  • セッションはクライアントとサーバーで状態を共有する仕組みである
  • サーバーはクライアントの状態を保持するため、ブラウザにCookieを発行する
  • CookieをExpressで使用するには「cookie-parser」モジュールを使用する

本章ではExpressによるHTTP通信の基礎を学びました。Lesson5ではさらに足を進め、Webアプリケーションとデータベースの接続について学びます。