Docker + Flask + Vue.jsでJSONをPOSTできない場合 - 加賀百万石ですが何か?

Docker + Flask + Vue.jsでJSONをPOSTできない場合

簡単なWebアプリを作ってみようと最近思い立ったのですが、バックエンド側にうまくPOSTできなかったので備忘録として現象と対処方法を書いておきます。

アプリの構成としては、

  • フロントで画像をアップロード
  • Base64に変換してJSONでバックエンドに送信
  • バックエンドでPillow画像に変換して画像認識
  • 認識結果の数値をJSONでフロントへ返信

というような流れになっています。

実装する上での技術要素としては主に以下の通りです。

  • Docker
  • Flask (Backend) on Docker container
  • Vue.js (Frontend) on Local machine (そのうちDockerコンテナ化予定)

発生した現象

まず発生した現象についてですが、以下の状況は確認済みでした。

  • Flaskのバックエンドをローカル or Dockerで立ち上げる
  • curlでPOSTした場合は正常に結果が返ってくる

この状態において、以下のようなエラー/現象が発生しました。

  1. ブラウザからaxiosでPOSTした場合に、Network Errorが発生した
  2. 1が解決した後にレスポンスが [object Object] になった

レスポンスはalert()で表示するようにしているため、1のNetwork Errorについてはこのようなエラー表示になります。

Network Error の対処方法について

これに関しては、通常は以下の「修正前」のようにFlaskを用いていると思いますが、「修正後」のようにFlask-CORSを使用すると解消しました。

修正前

from flask import Flask
app = Flask(__name__)

修正後

from flask import Flask
from flask_cors import CORS # ここを追加
app = Flask(__name__)
CORS(app) # ここを追加

ここでは解決方法のみの記載に留めますので、CORSの詳細を知りたい場合は調査してください。

レスポンスが [object Object] になる対処方法

これはそもそもREAT APIでJSONをやりとりする際の基本的な内容のようには思いますが、如何せんフロントエンドを真面目に触ってみたのが初めてだったので意外とハマりました。

まず表示される現象はこのような感じになります。

これはVue.js(というかJavaScriptの問題ですが)側で以下のように処理を修正すれば解消します。

修正前

axios.post(`${API_URL}/api/tidy-core/predict`, data, {
    headers: {
    'Content-Type': 'application/json'
    }
}).then(response => {
    alert(response.data)
}).catch(err => {
    alert(err)
});

修正後

axios.post(`${API_URL}/api/tidy-core/predict`, data, {
    headers: {
    'Content-Type': 'application/json'
    }
}).then(response => {
    alert(JSON.stringify(response.data))  // ここを修正
}).catch(err => {
    alert(err)
});

補足

このような処理でハマるのは僕のようにWebアプリの初心者に多いかと思います。そしてそのような方はそもそもどんな実装でデータを送受信すべきなのかよく分からないという方も少なくないかと思います。

そのため、全ソースを記載することはできないのですが、可能な範囲でデータの送受信部分の実装も記載しておきます。

フロントエンド:Vue.jsのJSON送信部分

onUploadImage () {
    var data = {
        img_base64: this.uploadedImage.toString('base64'),
        num1: 500,
        num2: 500
    };

    axios.post(`${API_URL}/api/tidy-core/predict`, data, {
        headers: {
        'Content-Type': 'application/json'
        }
    }).then(response => {
        //alert(JSON.stringify(response.data))
        alert(response.data)
    }).catch(err => {
        alert(err)
    });
},

バックエンド:Flaskの受信〜返信部分

def ANY_NAME():

    data = request.get_json()

    img_str = data['img_base64']
    num1 = data['num1']
    num2 = data['num2']

    """
    ANY PROCESS
    """

    return flask.jsonify({'SAMPLE_KEY': SAMPLE_VALUE})

スポンサーリンク