エンジニアを目指す日常ブログ

日々勉強したことのメモ。独学ですので間違っていたらコメント等で教えてください。

React+Node.jsとsocket.ioでチャットアプリの基礎を作成する

新しいエントリを履歴スタックにプッシュします React Router: Declarative Routing for React.js

https://tomiko0404.hatenablog.com/entry/%E2%96%A0

はじめに

今回の記事の目的

ReactとExpressを利用して簡易チャットアプリを作成した。 実施した内容をメモ/紹介する。 (躓いた点は別記事に切り出す。)

仕様

  • メッセージ入力エリアと送信ボタンがある。
  • 送信ボタンを押すとメッセージ入力エリアに入力した値を全員に送信する。
  • これまでに全員が送信したメッセージの一覧が表示されている。

アプリのイメージ。録画の都合上、1画面分しか撮れていませんが2つのブラウザを開いて交互に操作している。

前提

基本的には以下ここまでにやったことを辿って環境構築+サーバ作成を実施している。

利用している技術・モジュールは以下。

  • Node.js
  • Express
  • React(create-react-app)×Typescript
  • socket.io
  • socket.io-client

ここまでにやったこと

Step1 dockerを利用して画面サーバ用コンテナとWebサーバ用コンテナを起動した。

tomiko0404.hatenablog.com

Step2 Node.jsでWebHTTPサーバを作成して動かした。

tomiko0404.hatenablog.com

Step3 Reactで画面サーバを作成した。

tomiko0404.hatenablog.com

Step4 CORSを勉強して、画面サーバとWebサーバを接続できるようにした。

tomiko0404.hatenablog.com

Step5 socket.ioを利用して、画面サーバとWebサーバで相互通信できるようにした。

tomiko0404.hatenablog.com

チャットアプリ画面の見た目作成

画面の見た目を作成する。

Chakra UIの利用準備

コンポーネントライブラリのchakra-uiを利用する。 コンポーネントライブラリを使わずにHTMLを書いても問題ないが、簡単にそれなりの見た目が作れるため今回利用する。

Chakra UIのインストール

モジュールをインストールする。

$ yarn add @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4

公式ページ:Getting Started - Chakra UI

Providerの利用を宣言する

  • Chakraproviderをインポートする。
  • App.tsxから返却する値をすべて<ChakraProvider>で囲む。
import React from "react";
import "./App.css";
import { io } from "socket.io-client";
import { ChakraProvider } from "@chakra-ui/react";

function App() {
    // (過去記事参照)Webサーバとの接続を確立
    const socket = io("http://localhost:3000");

    //(過去記事参照) サーバに接続できた場合のイベント処理を定義する(過去記事参照)
    socket.on("connect", () => {
        console.log(`socket.connectを出力`);
        console.log(socket.connect()); // サーバに接続できたかどうかを表示
    });

    return (
        <ChakraProvider>
            <>
                <h1>画面サーバ</h1>
            </>
        </ChakraProvider>
    );
}
export default App;

見た目の作成

見た目を以下のように作成する。

`App.tsx(import文/return文の中身のみ記載)

import { ChakraProvider, Input, Button } from "@chakra-ui/react";
    return (
        <ChakraProvider>
            <>
                <ul>
                    <li>メッセージ1(仮)</li>
                    <li>メッセージ2(仮)</li>
                    <li>メッセージ3(仮)</li>
                </ul>
                <Input placeholder="なにか文字を入力してください" />
                <Button>送る</Button>
            </>
        </ChakraProvider>
    );

見た目が完成した。

チャットアプリの見た目
チャットアプリの見た目

クライアント側の処理を作成する

App.tsxへの記載内容

クライアントとサーバの相互通信を設定した際に記載したコードをベースに書き足す。

App.tsx は最終的に以下のようになる。

import React, { useState, useEffect } from "react";
import "./App.css";
import { io } from "socket.io-client";
import { ChakraProvider } from "@chakra-ui/react";
import { Input, Button } from "@chakra-ui/react";

// socketを接続する。引数にはWebサーバ側が待ち受けるドメインを指定する。
const socket = io("http://localhost:3000");
socket.on("connect", () => {
    console.log(`socket.connectの中身:`);
    console.log(socket.connect());
});

function App() {
    // メッセージリスト(全員が送ったメッセージの一覧)をStateとして定義
    const [messageList, setMessageList] = useState<string[]>([]);
    // インプットエリアに入力するメッセージをStateとして定義
    const [message, setMessage] = useState("");

    // サーバから"chat"イベントが送信されたときの処理
    // messageListに、Webサーバから受け取ったメッセージを追加する。
    useEffect(() => {
        socket.on("chat", (msg) => {
            setMessageList((messageList) => [...messageList, msg]);
        });
    }, []);

    // インプットエリアの文字が変更されたときの処理。
    // messageの値を都度変更する。
    const onChangeMessage = (e: React.ChangeEvent<HTMLInputElement>) => {
        setMessage(e.target.value);
    };

    // 「送る」ボタンを押したときの処理。サーバにmessageを送信する。
    const onClickSend = (
        e: React.MouseEvent<HTMLButtonElement, MouseEvent>
    ) => {
        e.preventDefault();
        socket.emit("chat message", message);
        setMessage("");
    };


    return (
        <ChakraProvider>
            <>
                <ul>
                    {messageList.map((data, index) => {
                        return <li key={index}>{data}</li>;
                    })}
                </ul>
                <Input
                    placeholder="なにか文字を入力してください"
                    value={message}
                    onChange={onChangeMessage}
                />
                <Button onClick={onClickSend}>送る</Button>
            </>
        </ChakraProvider>
    );
}

export default App;

解説

socket接続部分

Webサーバとの接続を確立する、const socket = io("http://localhost:3000");以下の文については、画面がレンダリングされるたびに実行される必要はなく、最初の1回のみ実行されればよい。

そのため、App関数の外に出しておいた。

インプットエリアに入力した文字をサーバに送る機能

送るボタンの設定

<Button>コンポーネントのonClickイベントにonClickSend関数を設定する。

<Button onClick={onClickSend}>送る</Button>
送るボタンをクリックしたときに起動する関数

onClickSend関数の処理内容は以下のようになっている。

// 「送る」ボタンを押したときの処理。サーバにmessageを送信する。
    const onClickSend = (
        e: React.MouseEvent<HTMLButtonElement, MouseEvent>
    ) => {
        e.preventDefault();
        socket.emit("chat message", message);
        setMessage("");
    };
引数

引数になっている(e)は、一般的なJavaScriptと同じでbuttonコンポーネントから受け取るイベントである。TypeScriptではeの型も指定する必要があるため、以下の記事を参考に設定している。

any型で諦めない React.EventCallback - Qiita

e.preventDefault()

次にe.preventDefault();は、「クリックなどのイベントに対し、ブラウザが勝手に規定している処理を実行させない」という指定である。

こちらの記事がわかりやすかった。
JavaScriptのpreventDefault()って難しくない?preventDefault()を使うための前提知識 - Qiita

socket.emit

一番重要なのがsocket.emitである。

 socket.emit([イベント名], 引数);

の構文でサーバに対してイベントを発行できる。

公式ページ:Client API | Socket.IO

socket.emit("chat message", message);

今回は、chat message というイベント名で、messageに入っている値をWebサーバ側に送信する。

messageを管理するstate

ここで、messageはインプットエリアに表示する値であり、「送る」ボタンを押したときにWebサーバ側に送る値である。この値は画面がレンダリングされるたびに初期化されては困るので、state管理する。

    // インプットエリアに入力するメッセージをStateとして定義
    const [message, setMessage] = useState("");

初期値は空文字""としている。

useStateのインポートも必要なので注意する。

import React, { useState} from "react";

インプットエリアの表示制御

インプットエリアの値が変更されたらその値を新たにmessageステートに格納し、更にインプットエリアには常にmessageの値を表示する(一般的な入力フォームの書き方)。

                <Input
                    placeholder="なにか文字を入力してください"
                    value={message}
                    onChange={onChangeMessage}
                />
    // インプットエリアの文字が変更されたときの処理。
    // messageの値を都度変更する。
    const onChangeMessage = (e: React.ChangeEvent<HTMLInputElement>) => {
        setMessage(e.target.value);
    };

メッセージの一覧を表示する機能

メッセージリストの管理

メッセージリストもStateとして管理する。

    // メッセージリスト(全員が送ったメッセージの一覧)をStateとして定義
    const [messageList, setMessageList] = useState<string[]>([]);

メッセージの一覧なので、初期値は空配列[]となる。型定義は<string[]>と書くことでstring型の配列を指定する。

サーバから「誰かがメッセージを送信した」イベントを受け取ったときの処理
    // サーバから"chat"イベントが送信されたときの処理
    // messageListに、Webサーバから受け取ったメッセージを追加する。
    useEffect(() => {
        socket.on("chat", (msg) => {
            setMessageList((messageList) => [...messageList, msg]);
        });
    }, []);
socket.on

重要なのはsocket.on関数である。

socket.on(イベント名, 関数)

の構文で、Webサーバから指定のイベント名のイベント受け取ったら、関数で定義した処理を実行することができる。今回は、誰かがメッセージを送信したら全員がWebサーバからchatイベントを受け取る。

公式ページ: Client API | Socket.IO

イベントを受け取ったときの処理
 setMessageList((messageList) => [...messageList, msg]);

useStateで定義されたsetState関数(ここではsetMessageList)には、値または関数を設定することができる。

これまでのmessageListの値(イベントを受け取る前までのメッセージ一覧)に対し、Webサーバから受け取った新規の発言をmessageListの一番後ろに追加する。

useEffectについて

「イベントを受け取ったときに実行する関数」をレンダリングのたびに定義していると、レンダリング量が増えすぎてしまった。結果、画面表示が非常に低速になってしまった。

そこで、useEffectを利用することで、レンダリングの数を抑えることにした。

実際、setState関数に設定する値の中に、そのstateを含む場合(今回の場合、setMessageListに設定する値に、messageListを利用する)は処理が難しくなるようで、かなり苦労した部分なので、別記事にて解説する。

useStateの使い方で苦しんだ別記事:
【React/ useState】setState関数の書き方が下手で大量レンダリングが起こった話 - エンジニアを目指す日常ブログ

メッセージリストの表示

こちらはメッセージリストから、'map'関数を利用して1個ずつ要素を取り出し<li>要素に入れているだけ。keyはインデックス(配列の何個目の要素かを表す)にしてしまったが、実際は「名前+送信時間」などにすべきと考えている。

                <ul>
                    {messageList.map((data, index) => {
                        return <li key={index}>{data}</li>;
                    })}
                </ul>

サーバ側の処理の作成

クライアントとサーバの相互通信を設定した際に記載したコードをベースに書き足す。

あるブラウザからメッセージを受け取ったら、全員に送信する機能

追記するのは以下の部分だけである。

    socket.on("chat message", (msg) => {
        io.emit("chat", msg);
    });

クライアント側のsocket.emit("chat message", message);で指定されたchat messageイベントを受けて、クライアント全体にchatイベントを送信する。

公式ドキュメント: Server API | Socket.IO

index.jsの記載内容

最終的にサーバ側のindex.jsの記載は以下のようになる。

const express = require("express");
const portNumber = 3000; // ポート番号
const app = express(); // Expressを利用したサーバ
const server = require("http").createServer(app); // Expressを用いないserverも必要

app.get("/", (req, res) => {
    res.status(200).send("OK!");
});

// サーバーオブジェクトsocketioを作成する
const { Server } = require("socket.io");
const io = new Server(server, {
    cors: {
        // Server作成時の引数にCORSオプションを追加する
        origin: "*",
        methods: ["GET", "POST"],
    },
});

io.on("connection", (socket) => {
    // ブラウザから接続されたときの処理
    console.log("a user connected");

    // ブラウザが切断したときの処理
    socket.on("disconnect", () => {
        console.log("user disconnected");
    });

    socket.on("chat message", (msg) => {
        io.emit("chat", msg);
    });
});

// serverをPORT3000で待ち受ける。app.listenだとNG。
server.listen(portNumber);
console.log(`Web server is on. PortNumber is ${portNumber}.`);

アプリ動作確認

ブラウザの接続

2つのブラウザを画面サーバに接続すると、Webサーバ側とsocket接続が2件確認できた。

Webサーバ側のログ
Webサーバ側のログ

メッセージの送信

左側のブラウザにメッセージを入れて送信すると、2つのブラウザにメッセージが表示された。

ブラウザに表示されるメッセージ
ブラウザに表示されるメッセージ

おわりに

socket.ioを利用してクライアント(ブラウザ)とWebサーバの接続を維持して、イベントを送信/受信できるようになった。

関連記事

useStateの書き方で苦しんだ部分

tomiko0404.hatenablog.com

つづき

入室機能とユーザ名設定機能を実装する

チャットアプリの見た目を修正する

画面サーバ(React)とWebサーバ(Node.js)をSocket.ioで接続する方法メモ

はじめに

今回の記事の目的

画面サーバ(React)とWebサーバ(Node.js)を別々に構築して、クライアントとWebサーバ間をSocket.ioで接続する。

チャットアプリを作る事前準備になる。

参考記事

  • 画面側をReactではなくindex.htmlで作成している記事。Reactで作る場合もこちらがベースとなる。 qiita.com
  • 公式ドキュメントでチャットアプリを作成するチュートリアル

Get started | Socket.IO

  • 公式ドキュメントのクライアントサイドの設定方法。Reactでのやり方は載っていない。

Client Initialization | Socket.IO

前提

環境は以下の記事の通り、Webサーバ用コンテナと画面サーバ用コンテナを用意し、各サーバを立てている状況。

tomiko0404.hatenablog.com

おそらくこんなイメージ。(間違っていたら修正します)

イメージ
イメージ

Webサーバ側の設定

socket.ioのインストール

socket.ioをインストールする。

$ yarn add socket.io

index.jsの変更

Webサーバ側のindex.jsは以下の記載とする。

index.js

// 基本設定
const express = require("express");
const portNumber = 3000;
const app = express(); // Expressを利用したサーバ作成
const server = require("http").createServer(app); // Expressを用いないserverも必要なので作成

// 別オリジンからのアクセスを許可する(CORSモジュール利用)
const cors = require("cors");
app.use(cors());

// HTTPアクセス時の応答を定義
app.get("/", (req, res) => {
    res.status(200).send("OK!");
});

// サーバーオブジェクトsocketioを作成する
const { Server } = require("socket.io");
const io = new Server(server, {
    cors: {                      // corsモジュールでは上手くCORSできないため、Server作成時の引数にオプションを追加する
        origin: "*",
        methods: ["GET", "POST"],
    },
});

// ブラウザから接続されたときの処理を定義する
io.on("connection", (socket) => { // ブラウザから接続されたときの処理
    console.log("a user connected");
    socket.on("disconnect", () => { // ブラウザが切断したときの処理
        console.log("user disconnected");
    });
});

// serverをPORT3000で待ち受ける。
// ※app.listenだとNG。
server.listen(portNumber);
console.log(`Web server is on. PortNumber is ${portNumber}.`);

解説

基本設定

以下の部分は、基本設定になるため過去の記事を参照。Expressサーバのappと、createServerで作ったserverの2つがあることに注意。listenする際はcreateServerで作ったserverを利用している。

// 基本設定
const express = require("express");
const portNumber = 3000;
const app = express(); // Expressを利用したサーバ作成
const server = require("http").createServer(app); // Expressを用いないserverも必要なので作成

// 別オリジンからのアクセスを許可する(CORSモジュール利用)
const cors = require("cors");
app.use(cors());

// HTTPアクセス時の応答を定義
app.get("/", (req, res) => {
    res.status(200).send("OK!");
});

// serverをPORT3000で待ち受ける。
// ※app.listenだとNG。
server.listen(portNumber);
console.log(`Web server is on. PortNumber is ${portNumber}.`);

過去の記事
WEBサーバと画面サーバを作成して相互に通信させる - エンジニアを目指す日常ブログ
Node.js + Express でWEB-APIサーバを作成する - エンジニアを目指す日常ブログ

クライアントとの接続

Socket.ioサーバの作成は以下の記載で実施する。

const { Server } = require("socket.io");
const io = new Server(server)

公式ドキュメント socket.io

CORSについて

CORSについては、corsモジュールを利用すれば問題ないかと思ったが、これでは接続できなかった。

そのため以下のドキュメントに従って設定を行なった。 socket.io

// const io = new Server(server)を書き換え
const io = new Server(server, {
    cors: {                      // corsモジュールでは上手くCORSできないため、Server作成時の引数にオプションを追加する
        origin: "*",
        methods: ["GET", "POST"],
    },
});

ブラウザからの接続時の挙動

以下の記載をすることで、ブラウザから接続された際にログを出力することができる。

io.on("connection", (socket) => { 
    console.log("a user connected");
});

また、接続しているときに切断された場合は再度ログを出力するためには、以下の記載に変更する。接続されたときの処理の中に、切断されたときの処理が記載されていることに注意する。

io.on("connection", (socket) => { // ブラウザから接続されたときの処理
    console.log("a user connected");
    socket.on("disconnect", () => { // ブラウザが切断したときの処理
        console.log("user disconnected");
    });
});

画面サーバ側の設定

socket.ioとsocket.io-clientをインストール

画面サーバにモジュールをインストールする。

$ yarn add socket.io-client

もしかしたらsocket.ioは不要かも。

$ yarn add socket.io

以下のページを参照。

Client Initialization | Socket.IO

ちなみに、socket.io-clientをインストールしない方法としてindex.htmlに以下記載を追加したり色々試してみたが、うまくいかなかった。

<script src="/socket.io/socket.io.js"></script>

App.tsxの変更

App.tsxに以下記載を追加する。

App.tsx

import React from "react";
import "./App.css";
// socket.io-clientをインポートする
import { io } from "socket.io-client";

function App() {
    // Webサーバとの接続を確立
    // ※Webサーバと別ドメインの場合には、io()に引数が必要
    const socket = io("http://localhost:3000");
   // サーバに接続できた場合のイベント処理を定義する
    socket.on("connect", () => {
        console.log(`socket.connectを出力`);
        console.log(socket.connect()); // サーバに接続できたかどうかを表示
    });

    return (
        <div className="App">
            <h1>画面サーバからの返却</h1>
        </div>
    );
}
export default App;

解説

io()

以下の記載でWebサーバに接続している。

    const socket = io("http://localhost:3000");

ドメインの異なるWebサーバに接続する際は引数にURLを入れる必要がある。

socket.on

    socket.on("connect", () => {
});

socket.on()は、イベント名と処理を引数に取る関数。イベント名は自分で決めることができる。このイベント名をキーにしてサーバと情報をやり取りする。

一方で、"connect"は事前に定義された特別なイベントであり、サーバに接続したことを示す。今回のように書くとサーバ接続時に実行したい処理を記載できる。 socket.io

socket.connect()

socket.connect()を利用すると、サーバへの接続時の情報を取得できる。 socket.io

接続できたかどうかの二値だけであれば、socket.connected()が使える。

接続結果

Webサーバと画面サーバを起動して、画面サーバlocalhost:8000にアクセスすると、以下のようにコンソールに接続情報が表示された。

ブラウザの表示
ブラウザの表示

また、画面サーバ側もコンソールログが表示されていることが確認できた。

$ node index.js
Web server is on. PortNumber is 3000.
a user connected
a user connected

※1回ブラウザにアクセスすると2回接続しているように見えるので要調査。

おわりに

画面サーバ(React)とWebサーバ(Node.js)を別々に構築して、クライアントとWebサーバ間をSocket.ioで接続することができた。

関連記事

つづき:チャットアプリの基礎を作成する

tomiko0404.hatenablog.com

WEBサーバと画面サーバを作成して相互に通信させる

はじめに

今回の記事の目的

自分で画面サーバとWebサーバを作成し、通信させる。 具体的には、画面サーバがWebサーバから値を取得して、画面表示する。

また、このときに必要になるCORSについて理解する。

前提

開発環境

  • ローカル環境はLinuxを利用する。

Windows上にLinux環境を構築する方法
Linux仮想環境:Vagrantの立ち上げ方メモ - エンジニアを目指す日常ブログ

docker環境

必須ではないが、今回は画面サーバ用のdockerコンテナとWebサーバ用のdockerコンテナを2つ用意した。

将来的にAWS ECSに載せたいのでこの構成にした。

dockerを立ち上げる方法メモ
tomiko0404.hatenablog.com

Webサーバの作成

WebサーバはNode.jsで作成した。

Webサーバの作成方法メモ。コンテナ上で実施しているので、「Node.js自体のインストール」は不要。 tomiko0404.hatenablog.com

画面サーバの作成

画面サーバはReact×Typescriptで構築した。

■Reactプロジェクトを作成する方法 tomiko0404.hatenablog.com
■Reactアプリを編集する方法 tomiko0404.hatenablog.com

全体構成

ローカルフォルダの構成は以下となっている。

├── back                                ※backendコンテナにマウントしている
│   ├── index.js
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   └── yarn.lock
├── docker
│   ├── Dockerfile
│   └── docker-compose.yml
└── front                                ※frontendコンテナにマウントしている
    ├── README.md
    ├── node_modules
    ├── package.json
    ├── public
    ├── src
    ├── tsconfig.json
    └── yarn.lock

Webサーバの内容

Webサーバは、実行するとポート3000番でリクエストを待ち受け、GETメソッドを受け取ると、ステータスコード200、ボディ部に"Hello World from backend!!!"を返却するアプリにする。

index.jsの記載内容は以下のようにする(後ほど、CORSのために追記が必要)。

index.js

// expressをrequireする
const express = require("express");

// ポート番号を変数に設定
const portNumber = 3000;

// appオブジェクトを作成する
const app = express();

// getメソッドで、ルートパスにアクセスしてきたときの処理を記載する
app.get("/", (req, res) => {
    res.status(200).send("Hello World from backend!!!");
});

// リクエストを待ち受ける
app.listen(portNumber);
console.log(`PortNumber is ${portNumber}`);

node index.jsを実行した後、ブラウザにlocalhost:3000を入力してアクセスすると、値が返ってきているのがわかる。

localhost:3000の応答
localhost:3000の応答

画面サーバの内容

画面サーバは、WebサーバにGETメソッドでHTTPアクセスして、応答された値をコンソールに表示した上で、画面にも表示するアプリとする。

axiosのインストール

HTTPで値を取得するために便利なaxiosモジュールをインストールする。

node@b0af091276ac:/app$ yarn add axios

App.tsxの作成

前提記事の通りにReact×Typescriptで作成した上で、今回、App.tsxを以下のように記載する。

App.tsx

import React, { useState } from "react";
import axios from "axios";
import "./App.css";

function App() {
    const [msg, setMsg] = useState("なし"); // msgステートを作成し、初期値は"なし"
    axios
        .get("http://localhost:3000") // localhost:3000にアクセス
        .then((res) => {
            console.log(res.data); // 取得した値をコンソールに表示
            setMsg(res.data); // msgステートに取得した値を格納
        })
        .catch((err) => console.log(err.responce));

    return (
        <div className="App">
            <h1>{msg}</h1> // h1の中身にmsgステートの値を表示
        </div>
    );
}
export default App;

getの中身はlocalhost:3000ではなくhttp://localhost:3000となることに注意する。(補完してくれるツールが多いため忘れてしまった)

Reactがわからない場合はこちらへ: Reactに入門した人のためのもっとReactが楽しくなるステップアップコース完全版 | Udemy

実際に画面サーバからWebサーバにアクセスすると、同一オリジンポリシーにより、はじかれる

画面サーバからWebサーバにアクセスする

Webサーバ側のコンテナでnode index.jsを実行した状態で、画面サーバ側でyarn startを実行する。

Webサーバから値をとることができていないことがわかる。

ブラウザに表示されるエラー
ブラウザのエラー

エラーの内容は、「CORSポリシーにブロックされており、応答のヘッダに'Access-Control-Allow-Origin'が無いです」というもの。

 Access to XMLHttpRequest at 'http://localhost:3000/' from origin 'http://localhost:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

CORS(Cross-Origin Resource Sharing) について

少し勉強が必要。私の理解で一言にすると以下(正確ではない)。

  • 基本的にWebの世界では同一オリジンポリシーというものがあり、プロトコルや、ドメインや、ポートが異なる人からはデータにアクセスできません。
  • ただし、アクセス先の設定で許可することができます。全員に許可することも、特定の人に許可することもできます。
  • 許可する場合は、レスポンスのヘッダにAccess-Control-Allow-Origin項目を返却します。

■CORSとはそもそも何かの参考
なんとなく CORS がわかる...はもう終わりにする。 - Qiita
■具体的なCORSの解説
こちらによれば「単純な要求」と「単純ではない要求」で対処が変わるとのこと。(今回は単純な要求)
CORS とは? - JavaScript の基本 - JavaScript 入門

ヘッダを確認

Postmanを利用してローカルからWebサーバにアクセスした際のレスポンスのヘッダを確認すると、Access-Control-Allow-Origin応答は無さそう。

Postman応答
Postman応答

異なるオリジンからのアクセスを許可する

すべてのAPIへのアクセスを許可する方法

CORS専用のモジュールがあるとのことなので、単純に使うのがよさそう。

corsモジュールインストール

Webサーバのdocker上で以下を実行する。

$ yarn add cors

index.jsの追記

以下の記載をindex.jsに追記する。

index.js

const express = require("express");

// CORSをインポートする
const cors = require("cors");

const portNumber = 3000;
const app = express();

// 全てのオリジンからのアクセスを許可する
app.use(cors());

app.get("/", (req, res) => {
    res.status(200).send("Hello World from backend!!!");
});

app.listen(portNumber);
console.log(`PortNumber is ${portNumber}`);

結果確認

ここで画面サーバ(localhost:8000)にアクセスしてみると、Webサーバから値を取得できたことがわかる。

ブラウザ確認結果
ブラウザ確認結果

Postmanでアクセスしてみると、ヘッダに

Access-Control-Allow-Origin: "*"

が追加されているのがわかる。

Postmanによるヘッダの確認
Postmanによるヘッダの確認

一部のAPIへのアクセスを許可する方法

個別のAPIへのアクセスを許可するには、getメソッドの引数に設定する。

app.get("/", cors(), (req, res) => {
    res.status(200).send("Hello World from backend!!!");
});

参考資料:
express.jsのcors対応 - Qiita

特定のオリジンからのアクセスのみ許可する場合

App.tsxを以下のように書き換える。

App.tsx

const express = require("express");
const cors = require("cors");
const portNumber = 3000;
const app = express();

// 一部のオリジンからのアクセスを許可する
const corsOptions = {
    origin: ["http://localhost:8000", "http://example.com"],
};
app.use(cors(corsOptions));

app.get("/", (req, res) => {
    res.status(200).send("Hello World from backend!!!");
});

app.listen(portNumber);
console.log(`PortNumber is ${portNumber}`);

参考資料:
ExpressとTypeScript APIにCORSサポートを追加する方法

おわりに

今回Webサーバと画面サーバを通信させることができた。また、CORSへの理解が深まった。

docker-composeを利用してdockerコンテナを作成する方法メモ(複数コンテナ対応)

はじめに

本記事の目的

dockerコンテナを立ち上げる。 また、今回はdocker-composeというツールも利用する。

基礎知識として、docker-composeツールを使わずにDockerを立ち上げる方法は以下の記事に記載している。 いかの記事ではAWSのEC2上で実施しているが、もちろんEC2を使わずローカルで実施も可能。
EC2サーバ(Linux)上でdockerコンテナを起動する方法メモ(nginxベース) - エンジニアを目指す日常ブログ

前提

  • Linux環境とする。

今回はWindows上にVagrantで仮想環境を用意している。 tomiko0404.hatenablog.com

dockerインストール

以下サイトを参考に実施した。

Vagrantの上にDockerを載せて開発環境を作成する(おそらくこちらを見ながらやったはず)
Ubuntuにdockerをインストールする - Qiita

docker-composeもインストールが必要。

Docker Compose のインストール | Docker ドキュメント

Dockerfileの作成

Dockerfileとは、dockerイメージファイルを作るときに元ネタとなる設定値を記載するファイル。 dockerイメージはdockerコンテナの設計図のようなもので、基本的にdockerは、

  1. Dockerfileに従ってdockerイメージをビルド
  2. dockerイメージに従ってコンテナを作成

という流れでコンテナを作成する必要がある。

今回はDockerfileを以下のように記述した。

FROM node:14.15.4-slim

RUN apt-get update && apt-get install -y locales \
    && locale-gen en_US.UTF-8 \
    && localedef -i en_US -f UTF-8 en_US.UTF-8 \
    && echo "export LC_ALL=en_US.UTF-8" >> ~/.bashrc \
    && ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

RUN apt-get install -y \
    wget \
    curl \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

一番上の

FROM node:14.15.4-slim

は、dockerhubという、コンテナイメージの倉庫のようなところから既存のイメージを持ってくるための記載。

今回はNode.jsを利用してアプリを作りたいので、Node.jsのイメージを持ってくる。

■docker hubの node ページ。 hub.docker.com ■nodeのReadmeページ。Dockerfileの書き方例なども載っている。 github.com

docker-compose.ymlファイルの作成

docker-composeは、一言でいえば「Docker構築を楽にするためのツール」である。

docker-composeツールを使うと、コマンド docker-compose up一つで「Dockerfileに従ってdockerイメージをビルド」⇒「dockerイメージに従ってコンテナを作成」を実行してくれる。

その際の設定値を規定するのがdocker-compose.ymlである。 dockerの各種コマンド(イメージをビルドするコマンドや、コンテナ起動するコマンド)の引数をdocker-compose.ymlで事前に定義しておくイメージ。

今回作成したdocker-compose.ymlファイル

今回は、「画面サーバ」と「Webサーバ」を用意したいので、2つのコンテナを立ち上げたい。 記載内容は以下となる。

docker-compose.yml

# このファイルはversion3の書き方仕様で書いてますよという宣言。おまじない扱いで良い。
version: '3'

# networkを作成する。複数のコンテナどうしでネットワークを構成したいときに
# 作成する。基本はbridgeを利用する。「react_net」は自分で設定したネットワーク名。
networks:
  react_net:
    driver: bridge

# 作りたいサービスを規定する。
# 今回は「frontend」と「backend」の2つのサービスを作成。
services:
  frontend:   # サービス名
    build: ./   # Dockerfileを利用してイメージをbuildする。Dockerfileの場所を記載する。
    image: react_front   #  buildしたイメージの名前を決める。
    container_name: frontend   # 起動するコンテナの名前を決める。
    tty: true   # 調査中
    volumes:
      - ../front:/app   # ローカルの ../front フォルダを、コンテナ上の/appフォルダにマウントする
    working_dir: "/app"   # コンテナが立ち上がったときに最初にいるフォルダの場所を決める
    ports:
      - 8000:8000   # ローカルの8000ポートとコンテナの8000ポートをポートフォワーディング
    networks:
      - react_net   # 上で定義したreact_netネットワークに接続する

  backend:
    build: ./
    image: node_back
    container_name: backend   # コンテナ名は別名を設定する
    tty: true
    volumes:
      - ../back:/app
    working_dir: "/app"
    ports:
      - 3000:3000 # ポートフォワーディングもかぶらないよう設定
    networks:
      - react_net

コンテナ1つで良い場合

コンテナ1つで良い場合は、以下の記載とする。

version: '3'

networks:
  react_net:
    driver: bridge

services:
  frontend:
    build: ./
    image: react_front
    container_name: frontend
    tty: true
    volumes:
      - ../:/app
    working_dir: "/app"
    ports:
      - 8000:8000
    networks:
      - react_net

docker-compose.ymlの記載内容について

各種設定の中身は以下記事が参考になる。 【Docker】初心者のための Docker Compose まとめ - AI can fly !!

参考:Dockerfileを利用しない方法

上記参考記事のimageオプションの解説を見ると以下の記載がある。

前述の build が指定されている場合は、 build で指定された Dockerfile から Docker イメージが構築され、そのイメージ名が image で指定した名前になります。 build を指定しない (Dockerfile からイメージを構築しない) 場合、ここで指定された Docker イメージからコンテナを作成しますが、指定した Docker イメージがローカルに存在しない場合は、 Docker Hub から Pull (取得) した上でコンテナが作成されます。

今回はbuildコマンドで自作のDockerfileを指定しているが、単純に

image: node:14.15.4-slim

と記載しても問題ない。この場合Dockerfileが不要になる。

イメージをビルドし、コンテナを作成する

(事前確認)dockerイメージの存在確認

今あるdockerイメージを確認する。

dockerイメージを確認するコマンド:docker images

$ sudo docker images

過去に作ったものが残っていた。nodeは公式イメージ。

REPOSITORY         TAG            IMAGE ID       CREATED        SIZE
react-practice-6   latest         d4a0b79d2c36   3 weeks ago    473MB
node               14.15.4-slim   2f75d89d8162   8 months ago   167MB

イメージ作成とコンテナ起動

以下のコマンドを実行する。

$ sudo docker-compose up -d

イメージが作られたことの確認

docker-compose.ymlで指定した名前のdockerイメージが作成された。

$ sudo docker images
REPOSITORY         TAG            IMAGE ID       CREATED              SIZE
node_back          latest         e6e824760f03   About a minute ago   216MB
react_front        latest         e6e824760f03   About a minute ago   216MB
react-practice-6   latest         d4a0b79d2c36   3 weeks ago          473MB
node               14.15.4-slim   2f75d89d8162   8 months ago         167MB

コンテナが起動したことの確認

  • 起動中のコンテナを確認するコマンド:docker ps
  • 起動中に限らずコンテナを確認するコマンド:docker ps -a

frontendコンテナとbackendコンテナが起動された。

$ sudo docker ps
CONTAINER ID   IMAGE         COMMAND                  CREATED         STATUS         PORTS                                       NAMES
fce6328cdc9f   react_front   "docker-entrypoint.s…"   3 minutes ago   Up 3 minutes   0.0.0.0:8000->8000/tcp, :::8000->8000/tcp   frontend
3eb02504a2a0   node_back     "docker-entrypoint.s…"   3 minutes ago   Up 3 minutes   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   backend

コンテナの中に入って操作する

コンテナの操作は基本的に'docker exec'で実施することができる。 毎回書くのは大変なので以下コマンドでターミナルを立ち上げることができる。

$ sudo docker exec -u node -it frontend /bin/bash -l

docker execの構文は以下の通りである。

$ docker exec [オプション] [コンテナ名] [コマンド] [引数...]

-u nodeはユーザ名を指定している。今回は公式のnodeイメージのデフォルトユーザ名nodeを指定。 -itは対話型を指定するオプション。 frontendはコンテナ名。 /bin/bash -lが実行したいコマンドとなる。

コンテナから抜ける

コンテナから抜けたいときはexitコマンドで抜けられる。

コンテナを停止する

コンテナを停止したいときは

$docker-compose down

【補足】コンテナ上でアプリを実行してみる

Node.jsで簡単なHTTPサーバが動くか確認。 コンテナと共有しているフォルダにapp.jsを作成する。

ローカルのフォルダ構成

ローカルのフォルダ構成は以下のようになっている。

.
├── back     ※backendのdockerにマウント
│   └── app.js
├── docker
│   ├── Dockerfile
│   └── docker-compose.yml
└── front      ※frontendのdockerにマウント(今は空)

app.jsの作成

app.js

const portNumber = 8000;
const http = require("http");

http.createServer((req, res) => {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Hello World");
}).listen(portNumber);

console.log(`PortNumber is ${portNumber}`);

app.jsを実行

コンテナに入った状態で以下を実行する。

node@fce6328cdc9f:/app$ node app.js

ブラウザからlocalhost:8000にアクセスすると応答が返ってくる。

応答が返ってくる
応答が返ってくる

今回は、Node.jsのみで実行できるアプリのため、app.jsを作成するだけで実行できる。追加モジュールが必要な場合は、npm installなどで追加する必要がある。

おわりに

docker-composeを利用してコンテナを立ち上げる方法をメモした。

以前にも同じような記事を書いたが、この理解度で複数のコンテナを立ち上げようとしたら行き詰ってしまったので、docker-composeについて勉強しました。 結果、理解が深まったので整理がてら書き直したのが本記事です。

以前の記事
vagrant上にdockerコンテナを立ち上げる方法メモ(docker-compose利用) - エンジニアを目指す日常ブログ

関連記事

つづき:WebAPIサーバの開発

上記のapp.jsの続きで、Node.jsでWebAPIサーバを作成した。

tomiko0404.hatenablog.com

つづき:フロントエンドの開発

dockerコンテナ上にReactアプリ作成した。

tomiko0404.hatenablog.com

参考資料

dockerコマンドが豊富な記事。

Dockerイメージとコンテナの削除方法 - Qiita

Linux仮想環境:Vagrantの立ち上げ方メモ

はじめに

今回の記事の目的

Windows上にLinuxの仮想環境を構築する方法として有名なVagrantの立ち上げ方メモ。

Vagrantと言っているが、仮想環境の実態はVirtualBoxで、VagrantVirtualBoxを操作するためのソフトらしい。

元々以下の記事に記載していたが、わかりにくいので切り出した。 tomiko0404.hatenablog.com

前提

今回は以下の前提とする。やり方を忘れてしまったので、別途復習したら記事にする予定。

  • VirtualBoxがインストールされていること。
  • Vagrantがインストールされていること。
  • Vagrantを用いたLinux環境構築ができていること。

参考になりそうな資料
kitsune.blog

vagrantを立ち上げる

Powershell(Window標準)を管理者権限で立ち上げる

Vagrantfileのあるフォルダに移動する

PS C:\WINDOWS\system32> cd C:\Users\User\vagrant\ubuntu64_18

vagrantの起動とssh接続

Vagrantの起動。

> vagrant up

VagrantSSH接続。

> vagrant ssh 

Vagrantをシャットダウンする際は、

> vagrant halt

とする。

Vagrantfileの中身

Vagrantfileには以下の記載がある。

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
  config.vm.network "forwarded_port", guest: 8000, host: 8000
  config.vm.synced_folder "./workspace", "/home/vagrant/workspace"
  config.vm.provider :virtualbox do |vb|
    vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/home/vagrant/workspace","1"]
  end

  config.vm.provider "virtualbox" do |vb|
    vb.memory = "1024"
  end

end

workspaceに移動する

Vagrantfileの中で

config.vm.synced_folder "./workspace", "/home/vagrant/workspace"

と記載しているため、ローカルの./workspaceフォルダ(Vagrantfileがある場所からの相対パス)と、Vagrant上の/home/vagrant/workspaceは同期されている。

扱いやすいように、このフォルダ上で作業をするのが良い。

$ cd workspace

でworkspaceフォルダに移動できる。このフォルダはWindows上からも操作可能。

おわりに

開発作業するたびにVagrantを起動する必要があるため、やり方をメモした。

手書きで書いた文字を画像に入れ込む方法

はじめに

今回の記事の目的

今回はプログラミングの勉強から少し離れる。 手書きで書いた文字の黒い部分を抽出し、データ化する方法を考えたのでメモ。

活用方法

手書きで味のある文字を写真の上などに乗せられたらかっこいいなと思い実施。 お祝いのプレゼントの添え物として、フォトブック(すぐアル|パレットプラザなど)を作り、そのうち1ページは写真の上に手書きの手紙が乗っているとオシャレではないかと思います。

両親の還暦祝いがあったので検討した。

完成イメージ
完成イメージ

前提

画像編集ソフトのGIMPがインストールされていること。

www.gimp.org

手書きの文字の準備

手書きの文字を用意。

  • なるべく白い紙に
  • なるべく黒いペンで書く。

書けたら写真を撮ってPCに転送する。

手書きの文字の写真
手書きの文字の写真

手書き文字から文字のみ抽出

写真をGIMPに取り込む

GIMPのウィンドウに画像をドラッグ&ドロップする。(上のバーから「ファイル」⇒「開く/インポート」でも良い)

画像を取り込んだところ
画像を取り込んだところ

トーンカーブの補正

右クリック⇒「色」⇒トーンカーブを選択。

トーンカーブ
トーンカーブ

背景部分を真っ白に寄せる

まずはトーンカーブ右上の点を、真左にスライドしていく。 すると、背景にしたい領域である、「元の画像で白に近いけど真っ白ではなかった部分」が、真っ白に近づいていく。

これくらいまでスライドすると、ノートの罫線が消えて文字と背景を白黒の2色に分けることができた。

背景を真っ白に寄せる
背景を真っ白に寄せる

もっとスライドすると画像の下のほうの陰になっている部分も白に近づけることができるが、ここはトリミングでざっくり消せそうなので、放置とする。

文字部分を真っ黒に寄せる

次にトーンカーブ左下の点を、真右にスライドしていく。 すると、文字領域である、「元の画像で黒に近いけど真っ黒ではなかった部分」が、真っ黒に近づいていく。

これで、文字の部分がだいぶはっきりした。変更後のカーブを見ると、変更後の明るさが明るさ最大(真っ白)と明るさ最小(真っ黒)の二値に近づいていることがわかる。

あああ
文字部分を真っ黒に寄せる

モードを白黒二値に変更する

画像を拡大してよく見ると、白と黒の中間の色が少しずつ残ってしまっているので、これらを完全に白or黒に寄せる。

拡大した図
拡大した図

画像⇒「モード」⇒「インデックス」を選択。

画像⇒「モード」⇒「インデックス」を選択
画像⇒「モード」⇒「インデックス」を選択

「Use black and white (1bit) palette」を選んで「変換」を押下。

Use black and white (1bit) palette
Use black and white (1bit) palette

これで拡大しても、白黒の二値画像となった。

拡大した図(修正後)
拡大した図(修正後)

背景(白部分)を削除する

アルファチャンネルの追加

これまで作業していた文字のレイヤーにアルファチャンネルを追加する。

レイヤーウインドウから、レイヤーを右クリック⇒「アルファチャンネルを追加」を押下する。

アルファチャンネルの追加
アルファチャンネルの追加

色を透明度に

上のバーから「色」⇒「色を透明度に」を選択する。これで白部分が透明となる。

色を透明度に
色を透明度に

モードをRGBに戻す

今後の編集を考えて、モードを「インデックス」から「RGB」に戻す。

完成

手書き文字だけ切り抜かれた画像が完成した。

完成図
完成図

下の影は矩形選択⇒消去でOK。

影の部分を消去
影の部分を消去

【応用編】写真の上に画像を乗せる

背景写真に文字を乗せる

写真に文字を乗せてみた。(注:写真は自分で撮影した画像です)

写真に文字を乗せた①
写真に文字を乗せた①

このままでも十分読めるが、写真によっては暗かったりして文字が読みにくい場合があるので、調整することも可能。

このように画像の明るい部分をより明るくしてもよい。

写真に文字を乗せた②
写真に文字を乗せた②

また、暗い部分を白っぽくし、コントラストを弱くすることで背景っぽい画像にしてもよい。こちらは人物が入った写真の時に有用な感覚が(私には)ある。

写真に文字を乗せた③
写真に文字を乗せた③

文字の周りに光彩を追加する

背景が暗い画像の時は文字のほうを工夫しても良い。 (注:この写真も自分で撮影した画像です)

背景画像が暗い場合
背景画像が暗い場合

光彩を追加してみる。

選択範囲の作成

レイヤーウインドウで、文字のレイヤーを選択し右クリック⇒「不透明部分を選択領域に」を押下する。

不透明部分を選択領域に
不透明部分を選択領域に

画像を右クリック⇒「選択」⇒「選択範囲の拡大」をクリックする。

選択範囲の拡大
選択範囲の拡大

拡大範囲は適宜調整だが、今回は4pxとした。

選択範囲拡大結果
選択範囲拡大結果

塗りつぶし

この状態で「新しいレイヤーを追加」し「光彩」レイヤーとする。

その後、新しいレイヤーを選択した状態で黄色で塗りつぶした。

黄色で塗りつぶし
黄色で塗りつぶし

ぼかし

画像を右クリック⇒「フィルター」⇒「ぼかし」⇒「ガウスぼかし」⇒2.5pxと選択していく。

ガウスぼかし
ガウスぼかし

レイヤー順変更

最後に文字レイヤーの下に光彩レイヤーを配置すると、光彩のようになる。

光彩の追加完了
光彩の追加完了

【参考】トーンカーブとは

画像の明るさを調整するツール。

トーンカーブ(デフォルト)
トーンカーブ(デフォルト)

  • 横軸が「今の明るさ」
  • 縦軸が「変更後の明るさ」

を示している。 また、うっすら見える面グラフは、選択中の画像のうち、「今の明るさ」の分布を示している。

横軸で、ある明るさを持つ領域を示す点を決めて、その点を上下に動かすと、「その領域を今より明るくしたいのか暗くしたいのか」を調整できる。

おわりに

手書きの文字を抽出するために、色を「白」「黒」の二値に分けてからの切り抜きを実施した。

今後活用していきたい。

【Node.js+Express】ミドルウェアとapp.useについてメモ

はじめに

今回の記事の目的

Expressで利用される「ミドルウェア」と、ミドルウェアを呼び出すときに使われるapp.useについて調べたことをメモ。

前提

  • Node.jsとnpm(Node.jsのパッケージマネージャ)がインストールされていること。

tomiko0404.hatenablog.com

  • Node.js + Expessを利用してHTTPサーバを立ち上げていること。

前回の記事 tomiko0404.hatenablog.com

ミドルウェアとは

Expressの公式ページには以下の記載がある。

Express ミドルウェアの使用

Express は、それ自体では最小限の機能を備えたルーティングとミドルウェアの Web フレームワークです。Express アプリケーションは基本的に一連のミドルウェア関数呼び出しです。

そもそもExpressはミドルウェアの呼び出しという機能がメインなんです、という話のようである。

ミドルウェアは、以下のように関数で定義される。

( req, res, next) => {
 処理
 next();
}

app.send()の引数にする関数と同様で、reqはhttp.IncomingMessageクラスのインスタンスであり、resはhttp.ServerResponseクラスのインスタンスである。 nextは次のミドルウェアを呼び出す関数である。

また、エラー処理を行なうミドルウェアは、以下のように引数が4つとなる。

// エラー処理を行うミドルウェア
( err, req, res, next) => {
 処理
}

ミドルウェアを利用するapp.use

ミドルウェアの種類

ミドルウェアには以下の種類があるらしい。

アプリケーション・レベルのミドルウェアは、app.use()などのappオブジェクトで利用できる。

const express = require('express');
const app = express();
app.use( (req, res, next) => {
  処理
  next()
})

ルーター・レベルのミドルウェアは、routerオブジェクトで利用できる。(routerオブジェクトについては未勉強のため今回は省略する。)

const express = require('express');
const router = express.Router();
router.use((req,res,nest) => {
  処理
  next()
})

app.use()について

app.use()は、リクエストの種類にかかわらず実行される関数を定義できる。

公式の記載は以下の通り。

指定されたミドルウェア関数を指定されたパスにマウントします。ミドルウェア関数は、要求されたパスのベースがパスと一致したときに実行されます。 Express 4.x - API リファレンス

全てのリクエストに対し処理する場合の記載は以下。

app.use( (req, res, next) => {
    処理
}

パスを指定する場合の記載は以下。

app.use("[パス]", (req, res, next) => {
    処理
}

app.use()を使ってみる

パスの指定なし

以下のようにapp.jsを記載し実行してみる。

app.js

const express = require("express");
const portNumber = 8000;
const app = express();

app.use((req, res, next) => {
    console.log("パス指定無し");
    next();
});

app.get("/",(req,res)=>{
    res.status(200).send("Hello!")
})

app.listen(portNumber);
console.log(`PortNumber is ${portNumber}`);

実行し、GETメソッドでlocalhost:8000にリクエストすると以下のようコンソールに表示された。

$ node app.js
PortNumber is 8000
パス指定無し

ちなみに、next();を記載し忘れると、コンソールログは表示されるが、レスポンスはエラーとなった。

パスを指定する

app.use( "/sample" , メソッド)を利用し、localhost:8000/samplelocalhost:8000/sample/abcなど、sample以下のURLにアクセスした場合の処理を記載する。

app.js

const express = require("express");
const portNumber = 8000;
const app = express();

// どこにアクセスしても実行
app.use((req, res, next) => {
    console.log("パス指定無し");
    next();
});

// /sampleの配下にアクセスしたら実行
app.use("/sample", (req, res, next) => {
    console.log("/sampleへのアクセス");
    next();
});

// ルートフォルダにGETしたら実行
app.get("/", (req, res) => {
    res.status(200).send("Hello!");
});

// /sampleフォルダにGETしたら実行
app.get("/sample", (req, res) => {
    res.status(200).send("Hello! /sample");
});

// /sample/abcフォルダにGETしたら実行
app.get("/sample/abc", (req, res) => {
    res.status(200).send("Hello! /sample/abc");
});

app.listen(portNumber);
console.log(`PortNumber is ${portNumber}`);

この状態でlocalhost:8000/sample/abcにアクセスすると、以下のようにコンソールに表示された。

$ node app.js
PortNumber is 8000
パス指定無し
/sampleへのアクセス

パス指定なしのapp.useと、"/sample"を指定したapp.useの処理がどちらも実行されている。また、順序はソースコードで呼び出した順になっている。

ミドルウェアを外部呼出し場合

以下の記事で関数を外部から呼び出したように、ミドルウェア関数も呼び出すことができる。

tomiko0404.hatenablog.com

おわりに

今回はアプリケーション・レベルのミドルウェアを作成、利用することができた。