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

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

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への理解が深まった。