クモのようにコツコツと

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

【JS】Fetch APIでPOST送信してみる(Form送信との比較も)

以前はFetch APIを使って外部のJSONデータを読み込みました。先日、それをAsync/Await構文に書き換えてみました。これらはあくまでデータの読み込みだけでしたが、今回はFetch APIを使ってデータをPOST送信をしてみます。Formタグからの送信との挙動の比較もしてみました。それではいきましょう!

【目次】

※参考:前回記事
【JSON】外部JSONファイル化したはっぴいえんどアルバム情報をFetch APIでDOMに読み込む - クモのようにコツコツと

※参考:※参考:ネイティブJSでいろいろやってみたシリーズ
qiita.com

今回作ったもの

今回作ったものはこちら。

f:id:idr_zz:20200731073240j:plain

上がformタグから送信するフォーム、下がFetch APIから送信するコード

※参考:プレビュー
FetchでGET送信

※参考:ソースコード
GitHub - ryo-i/postFetch: Fetch APIでGET送信してみる

Fetch APIからデータが送れる!

ふと、Fetch APIから外部からのデータを読み込むだけでなく、こちらからデータを送信できないかな、と気になった。

調べたところfetch()メソッドの第二引数にoptionを設定し、そこにbodyキーとして送信することができそう。

fetch(url , {
  method: "POST",
  body: data
}).then( // 続き処理…
  • 第一引数に送信先のURLを入れる
  • 第二引数は連想配列でオプションを設定する
  • methodオプションの値はPOST
  • bodyオプションの値に送信したいデータを入れる

※参考:Fetch を使う - Web API | MDN

GETとPOSTの違いはこちら。GETの場合はURLの後ろにパラメータとして付く。POSTは配列に入れて内部的に送信する。

※参考:フォームPHPはじめの一歩($_GETと$_POSTの違い) - クモのようにコツコツと

bodyに設定するデータはformのinputタグに入力した値にしたい。FormData()メソッドを使うとformのデータを取得することができるようだ。

let form = new FormData(form);

newでインスタンスを生成し、引数で紐付けるフォームを指定する。

※参考:FormData() - Web API | MDN

インスタンスについてはこちら。組み込みメソッドの中身を直接修正させないために生成する。

※参考:【JS】newとプロトタイプとクラス - クモのようにコツコツと

なお、送信先のURLはJSON PlaceHolderのURLにしてみた。*1

※参考:JSONPlaceholderを試してみた - A1 Blog

こちらの「post」ページがPOST送信専用に作られているページで、エラーにならなかった。

※参考:https://jsonplaceholder.typicode.com/posts/

HTMLコード

上記の方法で作ってみたコードがこちら。

※参考:ソースコード
GitHub - ryo-i/postFetch: Fetch APIでGET送信してみる

まずはHTMLコード

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>FetchでPOST送信</title>
</head>
<body>
    <section>
        <h1>あなたのお名前なんて〜の?</h1>
            <p>Formで答える</p>
            <form method="post" action="https://jsonplaceholder.typicode.com/posts/">
                <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>
  • 上のフォームはmethodpostに、actionはJSON PlaceHolderのPOST用のURLに
  • 上のフォームの送信ボタンはtypesubmit
  • 下のフォームはclass名fetchFormのみ
  • 下のフォームの送信ボタンはtypebutton

上のフォームはsubmitボタンを押すとformタグのpostメソッドが動いてactionのURLに入力データが送られる。

下のフォームは送信ボタンのtypeがボタンだし、formタグにもclass名しかないので何も起こらない。

JSコード

JSコード全体

下のフォームはJSコードで下記のFetch APIの処理によって入力データが送られる。

post_fetch.js

const fetchForm = document.querySelector('.fetchForm');
const btn = document.querySelector('.btn');
const url = 'https://jsonplaceholder.typicode.com/posts/';

const postFetch = () => {
    let formData = new FormData(fetchForm);
    for (let value of formData.entries()) {
        console.log(value);
    }

    fetch(url, {
        method: 'POST',
        body: formData
    }).then((response) => {
        if(!response.ok) {
            console.log('error!');
        } 
        console.log('ok!');
        return response.json();
    }).then((data)  => {
        console.log(data);
    }).catch((error) => {
        console.log(error);
    });
};

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

ボリュームがあるのでブロックごとに解説する。

DOM取得とURL指定

const fetchForm = document.querySelector('.fetchForm');
const btn = document.querySelector('.btn');
const url = 'https://jsonplaceholder.typicode.com/posts/';
  • 変数fetchFormで下のformタグ.fetchFormのDOMを取得
  • 変数btnで下の送信ボタン. btnのDOMを取得
  • 変数urlでJSON PlaceHolderのPOST用のURLを設定

postFetch()の大枠

変数postFetchはアロー関数でpostFetch()の処理が定義されている。

大枠としては下記の様な内容

const postFetch = () => {
    let formData = // フォームの入力データを取得

    fetch( 
        // APIを叩いてデータを送信
    );
};
  • 変数formDataでフォームの入力データを取得
  • fetch()メソッドでAPIを叩いてデータを送信

フォームデータを取得

    let formData = new FormData(fetchForm);
    for (let value of formData.entries()) {
        console.log(value);
    }
  • formDataFormData()インスタンスを生成しfetchFormの入力データを取得
  • for-of文でフォームデータの配列をvalueに取得してループし、コンソールに表示する

今回、APIがJSON PlaceHolderのダミーページのため、フォームデータを送っても正しいレスポンスを得られなかった。

そのため、FormData()でフォームデータを取得した時点でどんな値が取れたかをコンソールで確認した。

コンソールはconsole.log(fetchForm)と単純に入れても複雑な階層のオブジェクトがまるまる入ってしまい、値の確認が困難だった。

こちらの記事を参考にfor-of文を使って連想配列形式で表示した。

※参考:FormData オブジェクトの中身が見たい! - Qiita

for-of文は連想配列形式をループする制御構造。

※参考:for...of - JavaScript | MDN

FormData()につなげているentries()メソッドはフォームデータを連想配列形式に変換する。

The FormData.entries() method returns an iterator allowing to go through all key/value pairs contained in this object. The key of each pair is a USVString object; the value either a USVString, or a Blob.

FormData.entries()メソッドは、このオブジェクトに含まれるすべてのキーと値のペアを通過できるイテレータを返します。 各ペアのキーはUSVStringオブジェクトです。 USVStringまたはBlobのいずれかの値。

※参考:FormData.entries() - Web APIs | MDN

APIを叩いてデータを送信

次にFetch APIでAPIにデータを送信する。

    fetch(url, {
        method: 'POST',
        body: formData
    }).then((response) => {
        if(!response.ok) {
            console.log('error!');
        } 
        console.log('ok!');
        return response.json();
    }).then((data)  => {
        console.log(data);
    }).catch((error) => {
        console.log(error);
    });
  • fetch()の第一引数でurlのAPIにアクセス
  • fetch()の第二引数でオプションを設定
    methodPOSTにしてPOST送信
    bodyformDataを送る
  • then()でつなげてresponseを取得
    もしレスポンスが得られなかったらコンソールにerror!と表示
    レスポンスが得られたらコンソールにok!と表示
    response.json()でレスポンスをJSON形式に
  • then()でつなげてdataを取得し、コンソールに表示
  • catch()でAPIのアクセスがエラーの時にコンソールにエラーコードを表示

catch()はAPIのアクセスエラーしか感知しないので、それとは別に!response.okでレスポンスが得られなかった場合のエラーを感知する。

これでフォームデータ送信の通信結果がコンソールで一通り確認できる。

クリックイベント

最後にクリックイベントを設定

btn.addEventListener('click', postFetch, false);
  • btnをクリックした時にpostFetch()関数を実行する

ブラウザ動作確認

FormでPOST送信

こちらのGitHub Pagesで動作確認ができる。

※参考:FetchでGET送信

デベロッパーツールの「Network」タブを開いておく。 f:id:idr_zz:20200731194559j:plain

まずは上のフォームに値を入力する。上のフォームはForm送信される方。「孫悟空」と入れて送信ボタンを押すと… f:id:idr_zz:20200731195022j:plain

別ページに遷移する。JSON PlaceHolderのPOST用のダミーページ。 f:id:idr_zz:20200731195118j:plain

デベロッパーツール「Network」タブで「posts」を選択。Headers情報が見れる。 f:id:idr_zz:20200731195230j:plain

Headers情報の詳細についてはこちらを参照

※参考:ExpressとJSフレームワーク(React、Vue、Angularなど)との関係について調べたこと - クモのようにコツコツと

Headers情報の一番下までスクロールすると… f:id:idr_zz:20200731195234j:plain おお、「Form Data」で「name」キーの「孫悟空」という値が送信されている!

Fetch APIでPOST送信

今度は下のフォームでFetch APIから送信してみる。「ベジータ」と入れて送信ボタンを押すと… f:id:idr_zz:20200731200030j:plain

今後はページが遷移しない!「Network」タブには「posts」が増えている。 f:id:idr_zz:20200731200035j:plain

「posts」を開くとHeaders情報が見れる f:id:idr_zz:20200731200039j:plain

下にスクロールすると「Form Data」で「name」キーの「ベジータ」が送信されている! f:id:idr_zz:20200731200043j:plain

コンソールパネルを開く。 f:id:idr_zz:20200731200046j:plain

コンソールが3行表示されている! f:id:idr_zz:20200731200051j:plain

  • 1行目:FormDataを取得した直後のJSON形式の値。nameキーのベジータと正しく表示されている。
  • 2行目:ok!はレスポンスの取得に成功!
  • 3行目:レスポンスのJSON形式のJSON形式の値。idキーの101とJSON PlaceHolderの初期値しか取れていない。

Fetch APIで再送信

再度、下のフォームからFetch APIで送信してみる。今後は「フリーザ」と入れてみる。 f:id:idr_zz:20200731201209j:plain

Headers情報。Form Dataでnameキーから「フリーザ」が送信されている。 f:id:idr_zz:20200801061505j:plain

コンソール。「フリーザ」のフォームデータが表示されている。 f:id:idr_zz:20200731201218j:plain

最後に:次はDB、API側を体験したい!

ということでFetch APIでformタグと同じPOST送信に成功しました!ページ遷移やリロードしないのでユーザーのストレスが少ないのがいいですね。うまく使えばformタグやinputタグを使わなくてもPOST送信ができそうな気がします♪いいねボタンのオンオフとかね。

今回は送信先がJSON PlaceHolderのダミーページだったため、正しいレスポンス値を得ることができませんでした。

Node.jsやPHPなどを使ってバックエンド側のAPI部分を自作できると、意図通りのレスポンスを実現できそうです。

また、Firebaseを使うとAPI側の設定をブラウザ上で実現できるそうにも思っています。

次回はその前段階として、データベース自体の値の変更処理を体験したく思います。

それではまた!


※参考:※参考:ネイティブJSでいろいろやってみたシリーズ
qiita.com

*1:GitHub pagesのセキュリティ設定上、送信先を表示しているページ自体にしてもうまく動かなかった