Vue.js内のaxiosからPOSTすると2回POSTされてしまう


Chrome開発者ツールのNetworkで確認すると、確かに2回POSTしている……(片方はきちんとデータ送信できて200が返答、もう片方はブランクデータとしてサーバサイドで設定しているエラーが返ってくる)

下記のようなコードでVue.jsからaxiosでPOSTのメソッドを処理しているのですが、なぜか2回POSTが走ってしまうという謎現象が発生しています。

<template>
    <form @submit.prevent="send">
    <!-- フォーム部分略 -->
        <div class="form-group row">
            <div class="offset-sm-2 col-sm-10">
                <button type="submit" class="btn btn-primary"><i class="fas fa-fw fa-paper-plane" aria-hidden="true"></i>送信</button>
            </div>
        </div>
    </form>
</template>

<script>
import axios from 'axios'

export default {
//コンポーネントや他のプロパティ略
    data() {
        return {
            //他のデータ略
            senddata: {}
        }
    },
    methods: {
        send() {
            axios.post(URI + 'post/post', this.senddata) //senddataは送信ボタン押下時にはセットされている
                .then(response => {
                console.log(response)
            })
                .catch(error => {
                console.log(error)
            })
        }
    }
}
</script>

試したこと

  • [x] @submit.prevent="send"@submit.stop.prevent="send"に変更し、イベントの伝搬を停止するようにした
  • [x] 上記のイベント伝搬停止とは別にsendメソッドの中でevent.preventDefault()event.stopPropagation()を追加した
    • [x] sendメソッドの最後にreturn falseを追記。コールバックの中にもreturn false追記
  • [x] 親コンポーネントでもaxiosを読み込んでいる(同じimport axios from 'axios'文)なので、import axiosPost from 'axios'としてインスタンス名を変更した
  • [x] 送信ボタンをtype="submit"からtype="button"にして@click="send"で処理を実行するように変更
    • [x] type="submit"からtype="button"にし、さらにformタグをdivタグに変えて@submit.prevent="send"を削除、代わりに@click="send"を追加

結果

上記の試した内容全て効果なし。何が原因なのか……。

気付いたこと

send内にconsole.log('method in')など適当に出力させると、Chrome開発者ツールのConsoleには1度しか出力されていない。

また、レスポンスも1度しか出力されていない(エラーのcatchには入っていなさそう)ので、sendメソッドが重複してコールされているわけではなさそう。

1つ目のブランクデータで送信されてしまう方は、Request Methodが「OPTIONS」になっている
本来のデータが入っている正しい(と思われる)POSTはきちんとRequest Methodも「POST」になっている

よくよく見たら、Headerに上記画像のような違いが見られました。

対処

検索したところ、URLSearchParamsを使う方法がヒットしました。JSON形式ではなく従来のPOST形式でのデータ送信になってしまいイマイチ乗る気がしませんが、試したところ上手く行って本来の想定であるPOSTが1回だけになったので、とりあえずはこれで行こうかと思います。

コードは下記のように修正。

<template>
    <form @submit.prevent="send">
    <!-- フォーム部分略 -->
        <div class="form-group row">
            <div class="offset-sm-2 col-sm-10">
                <button type="submit" class="btn btn-primary"><i class="fas fa-fw fa-paper-plane" aria-hidden="true"></i>送信</button>
            </div>
        </div>
    </form>
</template>

<script>
import axios from 'axios'

export default {
//コンポーネントや他のプロパティ略
    data() {
        return {
            //他のデータ略
            senddata: null
        }
    },
    methods: {
        send() {
            axios.post(URI + 'post/post', this.senddata) //送信ボタン押下する時にはsubmitPrepareで値がセットされている
                .then(response => {
                console.log(response)
            })
                .catch(error => {
                console.log(error)
            })
        },
        submitPrepare() {
            const params = new URLSearchParams() //パラメータのインスタンス生成
            for(let key in this.formdata) {
                params.append(key, this.formdata[key].data) //キーに値を追加
                this.senddata = params //値セット
            }
        }
    }
}
</script>

修正後、POSTは1回だけになりました。

参考

追記 (2020/10/13)

今更と言えば今更ですが、この挙動は preflight request が飛んでいる挙動ですね、これ。

preflight request の挙動についてはCORS の挙動の観察と preflight request の検証の記事で追跡しました。

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。