クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【Express】body-parserでFetch API(およびForm)のPOST送信を受け取る

Expressの続きです。前回はMySQLとMongoDBに接続してみました。今回は「body-parser」を使って以前触れたFetch API(およびForm)のリクエストを受け取りレスポンスを返す動きを作ってみたく思います。それではいきましょう!

【目次】

※参考:前回記事
【Epxress】データベース接続の比較(MySQLとMongoDB) - クモのようにコツコツと

※参考:Node.js / Expressを習得するためにやったことまとめ
qiita.com

前回のおさらい

前回はExpressを使ってデータベースのMySQL、MongoDBに接続した。引き続き、接続したデータベースの内容をブラウザに返すAPIを作っていきたい。

※参考:【Epxress】データベース接続の比較(MySQLとMongoDB) - クモのようにコツコツと

以前、ExpressでURLへのリクエストに対して静的なHTMLファイル一式を表示させた。これはAPIというよりはApacheなどと同じWebサーバに相当する。

※参考:【Express】静的HTMLファイルの表示(res.sendFile()、express.static()) - クモのようにコツコツと

APIの場合は、FormやFetch APIなどによって送られてきたデータを受け取って、データベースなどに反映し、結果のJSONデータをレスポンスとして返したい。所謂「REST API」というやつ。

※参考:REST APIとは何かを調べまくったらようやくイメージができてきたのでまとめた - クモのようにコツコツと

そのためにはまず、ブラウザからのFormやFetch APIなどのデータを受け取る仕組みが必要。

Node環境のWebサーバと静的なファイルを準備

手順

まず最初にNode.js環境で構築したWebサーバにFetch APIのサンプを取り込む。サンプルは以前作ったこちら。

f:id:idr_zz:20200731073240j:plain

上のフォームがfornタグからの送信。下のフォームがFetch APIからの送信。

※参考:【JS】Fetch APIでPOST送信してみる(Form送信との比較も) - クモのようにコツコツと

Node.jsのWebサーバ構築方法はこちら。

※参考:Express事始め(インストール〜ハローワールドまで) - クモのようにコツコツと

※参考:【Express】静的HTMLファイルの表示(res.sendFile()、express.static()) - クモのようにコツコツと

Expressインストール

「express_api_test」というフォルダを作ってcdコマンドで移動。

cd /(フォルダのパス)/express_api_test

npmのpackage.jsonを作成

npm init -y

experssインストール

npm install express --save

index.jsを作成

const express = require('express');
const app = express();

app.use(express.static('public'));

app.get('/', (req, res) => {
    // es.sendFile(__dirname + '/public/index.html');
}); 

app.listen(3000, () => {
    console.log('Start server port:3000');
});

app.use(express.static('public'))public部分をページに表示する。

静的なファイルを追加する

「public」フォルダを作って、静的なHTMLファイルを入れる。「index.html」とjsフォルダの中に「post_fetch.js」。

f:id:idr_zz:20200830093707j:plain

この記事の時に作ったファイル一式ね。この時はjsonplaceholderと通信している。

※参考:【JS】Fetch APIでPOST送信してみる(Form送信との比較も) - クモのようにコツコツと

サーバ上の修正を常時監視したいのでnodemonでindex.jsを起動する。

nodemon index.js

ブラウザでローカルホスト(ポート番号3000)表示すると…

http://localhost:3000/

やた!ページが表示された!
f:id:idr_zz:20200828062726j:plain

body-parserとは

ブラウザからのFormのデータを受け取るためには「body-parser」というモジュールを使う。

Node.js body parsing middleware.
Parse incoming request bodies in a middleware before your handlers, available under the req.body property.

Node.jsボディ解析ミドルウェア。  
ハンドラーの前にミドルウェアで受信リクエスト本文を解析します。req.bodyプロパティで利用できます。

※参考:body-parser - npm

基本的な使い方

※参考:body-parser で フォームからの POST データを受け取る方法 - body-parser - Node.js 入門

FormだけでなくFetch APIのJSONデータも受け取れそう!

※参考:body-parser で JSON 形式の HTTP リクエストを受け取る方法 - body-parser - Node.js 入門

※参考:FetchAPIでHTMLからNode.jsに非同期でJSONパラメータを送る | 株式会社ブリッツゲート

自分的にはページ遷移やリロードをしないFetch APIの活用に興味があるため、こちらも体験したい。

body-parserインストール

何は無くともインストール

npm install body-parser

package.jsonを確認。入ってる!

  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1"
  }

index.jsにインポートする。

const bodyParser = require('body-parser');

Form送信の設定

formタグ修正

まずformタグを修正

            <form method="post" action="/form">
                <input type="text" name="name" size="30" maxlength="30">
                <input type="submit" value="送信">
            </form>

前回、action属性がjsonplaceholderのURLだったが、今回はページの下層/formに送信する。

bodyParser.urlencoded()

次にindex.jsでformとのAPI通信を設定する。

const urlencodedParser = bodyParser.urlencoded({ extended: false });
  • 変数urlencodedParserの引数でbodyParser.urlencoded()を実行(引数は連想配列でextendedキーをfalseに)

bodyParser.urlencoded()はURLエンコードからbody情報だけを抜き出すメソッドのようだ。

Returns middleware that only parses urlencoded bodies and only looks at requests where the Content-Type header matches the type option. This parser accepts only UTF-8 encoding of the body and supports automatic inflation of gzip and deflate encodings.

URLエンコードされた本文のみを解析し、Content-Typeヘッダーがタイプオプションと一致するリクエストのみを確認するミドルウェアを返します。 このパーサーは本文のUTF-8エンコーディングのみを受け入れ、gzipおよびdeflateエンコーディングの自動インフレーションをサポートします。

※参考:body-parser - npm

urlencoded()の引数の中はオプション

{ extended: false }

extendedオプションは拡張オプションでfalseにするとクエリ文字列ライブラリにに変換されると。

The extended option allows to choose between parsing the URL-encoded data with the querystring library (when false) or the qs library (when true). The "extended" syntax allows for rich objects and arrays to be encoded into the URL-encoded format, allowing for a JSON-like experience with URL-encoded. For more information, please see the qs library.

拡張オプションを使用すると、URLエンコードされたデータをクエリ文字列ライブラリ(falseの場合)またはqsライブラリ(trueの場合)のどちらで解析するかを選択できます。 「拡張」構文により、リッチオブジェクトと配列をURLエンコード形式にエンコードできるため、URLエンコードされたJSONのようなエクスペリエンスを実現できます。 詳細については、qsライブラリを参照してください。

※参考:body-parser - npm

formのAPI設定

次はformのAPI設定

app.post('/form', urlencodedParser, (req, res) => {
    console.log(req.body);
    const name = req.body.name;
    console.log('name->' + name);
    res.send('こんにちは!' + name + 'さん');
});
  • app.post()を実行。第一引数はパス/form、第二引数でurlencodedParser実行、第三引数は無名関数
  • 無名関数の第一引数はリクエストreq、第二引数はレスポンスres
  • コンソールでリクエストのボディ情報req.body表示
  • 変数nameでボディ情報のnameキーの値を取得
  • コンソールでnameも表示
  • res.send()でテキスト情報「こんにちは〇〇さん」のレスポンスを返す

app.post()はPOST送信の指定パスにルーティングするメソッド

Routes HTTP POST requests to the specified path with the specified callback functions.

指定されたコールバック関数を使用して、HTTP POST要求を指定されたパスにルーティングします。

※参考:Express 4.x - API リファレンス

res.send()はこちらの記事でも触れたように、いろいろなレスポンスが返せる便利メソッド。

※参考:【Express】静的HTMLファイルの表示(res.sendFile()、express.static()) - クモのようにコツコツと

第一引数でformタグのaction属性と同じ/formを指定し、第二引数でurlencodedParserを実行する。第三引数でformからきたnameの値を抜き出して「こんにちは〇〇さん」というテキストを返す。

Fetch APIの通信設定

次はFetch APIの設定をしてみる。

Fetch API設定の変更

まず、フロントエンド側のJSコード(post_fetch.js)のFetch API設定を修正

const fetchForm = document.querySelector('.fetchForm input');
const btn = document.querySelector('.btn');
const url = '/fetch';
  • 変数fetchFormをformタグではなくinputタグに
  • 変数url/fetch

inputタグにしたのはinputタグの値をJSONデータ自体を送りたかったため。(forからのbody情報だとうまくいかなかった。。)

また、変数urlは先ほどのformタグと同様jsonplaceholderのURLだったが下層の/fetchに変更した。

次にpostFetch()のFetch APIの設定を変更

const postFetch = () => {
    const formData = {
        name: fetchForm.value
    };

    fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(formData)
    }).then((response) => {
        if(!response.ok) {
            console.log('error!');
        }
        console.log('ok!');
        console.log(response);
        return response.text();
    }).then((data)  => {
        console.log(data);
        alert(data);
    }).catch((error) => {
        console.log(error);
    });
};
  • 変数formDataで連想配列を作成。nameキーにinputタグの値を入れる
  • fetch()のオプションm、headers'Content-TypeがJSONデータと設定
    bodyJSON.stringify()で連想配列をJSONデータに変換して送信
  • 一つ目のthen()、レスポンスはJSONではなくテキストresponse.text()で受け取る
  • 二つ目のthen()、レスポンスをアラートに表示

全体的にこちらの記事でやったように、formデータではなくJSONデータで送信する方法に書き換えた。

※参考:【JS】Fetch APIを使ってJSON ServerにCRUDする - クモのようにコツコツと

また、レスポンスをresponse.text()にしたのは、後述するようにindex.jsのAPI設定で先程のformと同様JSONではなく「こんにちは〇〇さん」というテキストを返すため。

なお、最後のイベント設定は変わらない。送信ボタンを押した時に発火。

btn.addEventListener('click', postFetch, false);

bodyParser.json()

次に、index.jsのAPI通信を設定。

const jsonParser = bodyParser.json();
  • 変数jsonParserbodyParser.json()を実行

bodyParser.json()はJSONを解析するメソッド。

Returns middleware that only parses json and only looks at requests where the Content-Type header matches the type option. This parser accepts any Unicode encoding of the body and supports automatic inflation of gzip and deflate encodings.

jsonの解析のみを行い、Content-Typeヘッダーがタイプオプションと一致するリクエストのみを確認するミドルウェアを返します。 このパーサーは、本文のUnicodeエンコーディングを受け入れ、gzipおよびdeflateエンコーディングの自動インフレーションをサポートします。

※参考:body-parser - npm

Fetch API設定

次にFetch APIの送受信設定。

app.post('/fetch', jsonParser,  (req, res) => {
    console.log(req.body);
    const name = req.body.name;
    console.log('name->' + name);
    res.send('こんにちは!' + name + 'さん');
});
  • app.post()の第一引数のパスを/fetch
  • 第二引数でjsonParserを実行
  • 第三引数のリクエスト、レスポンス設定は先程のformの処理と同じ

パス(/fetch)と実行するメソッド(jsonParser)以外は変わらない。

動作確認

formの挙動

作ったものの挙動

ブラウザ(localhost:3000)のフォーム f:id:idr_zz:20200830082609j:plain

まずは上のformタグに「孫悟空」と入力して「送信」すると… f:id:idr_zz:20200830082612j:plain

ページが遷移(localhost:3000/form)して「こんにちは!孫悟空さん」と表示された! f:id:idr_zz:20200830082615j:plain

Dev-toolsの「Network」タブで「form」の内容を確認する。 f:id:idr_zz:20200830082618j:plain

「Headers」の下の方「Form Data」nameキーの「孫悟空」が送信されている。 f:id:idr_zz:20200830082620j:plain

「Response」にも「こんにちは!孫悟空さん」が表示されている。 f:id:idr_zz:20200830082624j:plain

Node環境(index.js)のコンソールにも送信されたデータが表示されている。

[Object: null prototype] { name: '孫悟空' }
name->孫悟空

このname「孫悟空」を抜き出してテキストに入れ込んで返したということ!

Fetch APIの挙動

次に下のフォームに「ベジータ」と入れて送信。 f:id:idr_zz:20200830092149j:plain

「こんにちは!ベジータさん」のアラートが表示された!Fetch APIなのでページ遷移しない♪ f:id:idr_zz:20200830092152j:plain

Dev-toolsの「Network」で「fetch」を確認する。 f:id:idr_zz:20200830092156j:plain

「Headers」の下の方、JSON形式でnameキーの値ベジータが送信されている。 f:id:idr_zz:20200830092200j:plain

「Response」を見ると「こんにちは!ベジータさん」のテキストが返されている。 f:id:idr_zz:20200830092209j:plain

「Console」にはレスポンスとテキスト「こんにちは!ベジータさん」が表示されている。 f:id:idr_zz:20200830092203j:plain

レスポンスは階層が深く膨大だがここからテキストだけが抽出できている。 f:id:idr_zz:20200830092206j:plain

Node環境(index.js)のコンソールにも送信されたデータが表示されている。

{ name: 'ベジータ' }
name->ベジータ

コード全体

作ったものこちら

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ExpressにPOST送信(Form & Fetch)</title>
</head>
<body>
    <section>
        <h1>あなたのお名前なんて〜の?</h1>
            <p>Formで答える</p>
            <form method="post" action="/form">
                <input type="text" name="name" size="30" maxlength="30">
                <input type="submit" value="送信">
            </form>
            <p>Fetchで答える</p>
            <form class="fetchForm">
                <input type="text" name="name" size="30" maxlength="30" class="name">
                <input type="button" value="送信" class="btn">      
            </form>
    </section>
    <script src="js/post_fetch.js"></script>
</body>
</html>

post_fetch.js

const fetchForm = document.querySelector('.fetchForm input');
const btn = document.querySelector('.btn');
const url = '/fetch';

const postFetch = () => {
    const formData = {
        name: fetchForm.value
    };

    fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(formData)
    }).then((response) => {
        if(!response.ok) {
            console.log('error!');
        }
        console.log('ok!');
        console.log(response);
        return response.text();
    }).then((data)  => {
        console.log(data);
        alert(data);
    }).catch((error) => {
        console.log(error);
    });
};

btn.addEventListener('click', postFetch, false);

index.js

const express = require('express');
const bodyParser = require('body-parser');
const app = express();

app.use(express.static('public'));

// Form設定
const urlencodedParser = bodyParser.urlencoded({ extended: false });

app.post('/form', urlencodedParser, (req, res) => {
    console.log(req.body);
    const name = req.body.name;
    console.log('name->' + name);
    res.send('こんにちは!' + name + 'さん');
});

// Fetch API設定
const jsonParser = bodyParser.json();
 
app.post('/fetch', jsonParser,  (req, res) => {
    console.log(req.body);
    const name = req.body.name;
    console.log('name->' + name);
    res.send('こんにちは!' + name + 'さん');
});

app.listen(3000, () => {
    console.log('Start server port:3000');
});

GitHub

※参考:GitHub - ryo-i/express_body_parser: 【Express】body-parserでFetch API(およびForm)のPOST送信を受け取る

最後に

ということで、FormタグやFetch APIから送信されたデータをbody-parserで受け取って、「こんにちは!○○さん」というテキストにして返しました!

ユーザーから見たらなんでもない挙動なんですが、自分としてはこの動きがブラウザ 側だけでなくサーバ(Node環境)とのやり取りを経て実現できていることに大きな意義がありました!
また、formタグだけでなくページ遷移しないFetch APIの非同期通信ともやり取りできてよかったです♪

次はブラウザから送られたデータをサーバ(Node環境)からデータベース(MySQL)にCRUD処理して、さらにその結果をブラウザに表示する、という体験に入って行きたく思います。

それではまた!


※参考:Node.js / Expressを習得するためにやったことまとめ
qiita.com