見出し画像

Webサイトは、情報をどこでどう覚えているのか 〜セッション、クッキー、永続化〜

プログラム自学案内の24回目です。今回は、Webプログラミング特有の概念、セッションとクッキーについて紹介します。 前回までの記事はこちら。

さっそくサンプルから

さっそくサンプルコードを紹介しますので、実際に読者の手元で動かしてみてください。

セッション という言葉の意味は、そのあとで説明します。

前提条件

今回のサンプルは、前回の記事で作成したWebフォームつきのflatter-webにさらに機能を追加します。前回記事のコードが動くことを確認しておいてください。課題への解答はできていなくても構いません。

はじめに ライブラリの追加

Express.jsで セッションを扱うには、express-session (https://github.com/expressjs/session#readme) というライブラリが追加で必要になります。ですので、まずはこのパッケージをインストールしてください。

npm install express-session

つづいて セッションの設定を追加

つづいて、このライブラリを利用するように アプリケーション設定を追加します。

app.js (抜粋)

const express = require('express');
const mustacheExpress = require('mustache-express');
const logger = require('morgan');
const session = require('express-session');

// 中略

app.use(logger('dev'));
app.use(session({
    secret: 'a random set of characters like 1aq2ws3de4',
    resave: false,
    saveUninitialized: true
}));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(__dirname + '/static'));

app.use('/', router);

追加されているのは、const session = require('express-session'); のくだり、app.use(session({ …. })); のくだりの2か所です。

いよいよ セッションを利用した処理を追加

ここまでの設定が終わると、リクエストオブジェクトの sessionプロパティ(req.session)から、セッションを操作できます。

次のコードでは、セッションごとに管理された記憶領域に history という名前の、「これまでおだてた人」を格納する配列を作成し、読み書きする処理を追加しています。

さらに、'/session-destroy' というパスを新たに作り、セッションを破棄する操作を追加しています。

controllers/router.js

const express = require('express');
const router = express.Router();
const flatter = require('../models/flatter');
const namelist = require('../models/namelist');

router.get('/', function (req, res, next) {
    const data = namelist();
    data.history = req.session.history;
    res.render('list_view.html', data);
});

router.get('/flatter/:name', function (req, res, next) {
    const data = flatter(req.params.name);
    pushHistory(req.session, req.params.name);
    res.render('flatter_view.html', data);
});

router.get('/flatter-by-query', function (req, res, next) {
    const data = flatter(req.query.name);
    pushHistory(req.session, req.query.name);
    res.render('flatter_view.html', data);
});

router.post('/flatter-by-post', function (req, res, next) {
    const data = flatter(req.body.name);
    pushHistory(req.session, req.body.name);
    res.render('flatter_view.html', data);
});

router.get('/session-destroy', function (req, res, next) {
    req.session.destroy();
    res.redirect('/');
});

function pushHistory(session, name) {
    if (!session.history) {
        session.history = [];
    }
    session.history.push(name);
}

module.exports = router;

次のコードでは、「これまでおだてた人」と、それをリセットするためのリンクを表示するコードを追加しています。

views/list_view.html (抜粋)


    <form method="POST" action="/flatter-by-post">
        おだてる人: 
        <input type="text" name="name"> さん
        <button type="submit">リクエストボディで送信</button>
    </form>
    <hr>
    <br>
    あなたが いままでに おだてた人たち
    <ul>
        {{#history}}
        <li>{{.}}さん</li>
        {{/history}}
    </ul>

    <a href="session-destroy">リセット</a>

</body>

</html>

挙動の確認

おだてるたびにホーム画面にその履歴が追加され、リセットするとその履歴が全て消えるようになるはずです。

おだてた人たちの履歴が表示される

セッションとは

セッション(Session) とは、サイト訪問者がWebサイトを最初に訪問してから、サイトを去るまでの一連のやり取りや、そこで共有される情報を言います。典型的には、サイトへのサインインからサインアウトまでの一連のやり取りが1つのセッションと言えます。

セッションの主な特徴は、つぎの2つです。

  • 複数のHTTPリクエスト/レスポンスをまたぐ

  • 別々の訪問者に対しては、それぞれ別々のものとして扱われる

2つ目の特徴については、1人であっても、別々のブラウザを立ち上げたり、同じブラウザでもゲストモードでアクセスすることによって、別人としてサイトにアクセスし、確かめることができます。

ところで、Webサーバは、ほうぼうから沢山のリクエストを受け取っています。そのうち、あるリクエストと、別のリクエストが、同じセッションである(同じ人との一連のやり取りである)、と、サーバはなぜ見分けられるのでしょうか?

ここでクッキーの話が出てきます。

クッキーとは

クッキー(Cookie、HTTP Cookie) は、Webサーバが、Webサイトを訪問する利用者のPCやスマホにデータを読み書きするためのしくみです。

ですが、もし、Webサイトを訪問したときに、Webサーバが勝手に無制限に、訪問者のPCやスマホの情報を読み書きできるとしたら、怖くてだれもWebサイトに近づけませんよね。

そこで、ブラウザが訪問先のサイトごとに専用のファイルやディレクトリを設け、Webサーバによるデータの読み書きは、その場所限定で、かつ、ブラウザに代行させるような制限のある仕組みがつくられています。この仕組みがクッキーです。ブラウザに読み書きを代行させる指示は、HTTPリクエストとHTTPレスポンスを通じて行われます。

セッションとクッキーの関係

Webサーバは、セッションを見分けるためにクッキーを使います。

具体的には、Webサーバは、クッキーのしくみを使って、訪問者のPCやスマホに、セッションを見分けるための目印を書き込みます。この目印をセッションIDと言います。こうしておくことで、リクエストの送信元のPCやスマホから読み取られた目印が、以前書き込んだ目印と一致したら、同じセッションのリクエストだと分かるというわけです。実際は、このセッション判別の処理は、express-session などのライブラリがやってくれるのですね。

興味がある方は、サンプルコードを動かして、ブラウザにより自分のPCに書き込まれたクッキーや、HTTPリクエストやレスポンスの内容を確認してみてください。クッキーの確認の仕方や、HTTPリクエスト、レスポンスの確認の仕方が分からない方は、ぜひそのやり方も調べてみてください。

ここらへんのしくみは、知らなくても良いかもしれませんが、覚えておいて損は無いです。

Webサイトは、情報をどこでどう覚えているのか

記事の最後に、Webサーバが読み書きする情報の置き場所を、整理してみましょう。

Webサーバが読み書きする情報のありかは、ざっくり言うと、絵に示した5つの領域に分けて考えることができます。何をどこの領域に置けば良いのか、これが、Webサーバアプリの開発者にとっては、思案のしどころなのです。

まず大きくデータの置き場所として、1から4がWebサイト側、5が訪問者側と分かれます。さらに、1から4の番号は、そのまま読み書きされるデータが保管される期間、すなわちデータの寿命の短い順番に分かれています。

1. リクエストスコープ

リクエストを受け取ってからレスポンスを返すまで存在する領域です。レスポンスが返されると、この領域の情報をWebサーバは忘れます。

サンプルコードで具体的に言うと、router.jsの function(req, res, data) や、flatter.js の flatter(name){} の引数や、その内部で定義された変数がこの領域にあります。

2. セッションスコープ

セッションごとに作られる領域です。セッションが終わると、この領域の情報をWebサーバは忘れます。

サンプルコードでは request.session がこれにあたります。

※ なお、絵やサンプルでは Webサーバの内部記憶にセッションスコープがありますが、これは入門記事のため簡易な方法をとっているためです。本来はセッションの情報はWebサーバの外部(絵では右下の「外部記憶」の場所)に置く方が良いとされています。

3. アプリケーションスコープ

Webサーバが起動してから停止するまで存在し続ける領域です。Webサーバが停止すると、中の情報が消えます。

サンプルコードではapp.jsで定義された変数や、router.jsの関数の外側にある変数がこの領域にあります。この領域のデータはほとんどの場合、サーバ起動時に作られ、そのあとはサーバが停止するまで読み取られるだけという感じです。

4. 永続化されたデータ

Webサーバが停止したり、電源が落ちても、存在し続ける領域です。

ユーザの投稿内容、注文内容などの大事なデータは、電源を落としても残る外部記憶装置(ハードディスクやSSDなど)に記録しておく必要があります。これを 永続化(persistence) と言います。

サンプルコードでは、まだ、ここへの情報の読み書きはしていません。やりかたは今後の記事で紹介する予定です。

5. クッキー

ネットをはさんだ向こう側、Webサイトを訪問する利用者のPCやスマホにあるデータです。

ここにあるデータはブラウザの利用者が消せるどころか、悪意を持って付け加えたり改ざんしたりできるため、ここにあるデータの中身はあまりアテにできないというのが大きな特徴です。

まとめと次回予告

今回はWebアプリのツボ、クッキーとセッションについて紹介しました。

次回予告です。よくある入門コースでは、このあとデータの永続化のポピュラーな方法、RDBMSの利用法を紹介していく感じになるわけですが、この連載ではそのまえに、作ったWebアプリケーションをインターネットに公開する方法を紹介します。だって、自分が作ったものをネットに公開して、友人に自慢したり、SNSで「いいね」もらったりしたいじゃないですか。

RDBMSの利用法はその後からのお楽しみです。

#コラム #プログラミング #独学 #案内 #Express.js #セッション #クッキー   #express -session #session #cookie


この記事が気に入ったらサポートをしてみませんか?