Help us understand the problem. What is going on with this article?

FlaskでAPI作って素のJavaScriptでAPI実行する

More than 1 year has passed since last update.

シンプルにFlaskでAPIを作って、jQueryとかAjaxとか使わない素のJavaScriptでAPIを実行してブラウザ上で表示したいときのまとめ。
JavaScript単体では難しい処理を実装したいときや、Pythonで実装した処理のフロントを手っ取り早く実装したいときに使えると思います。

TL;DR

  • JavaScriptのFetch APIでAPI実行する
  • Flask(Python製のウェブアプリケーションフレームワーク)でAPI自作する
  • おまけで、Flask環境をDockerで作る場合のファイル一式紹介

ブラウザでの実行例の画像
image.png

ちょっと作り込んだバージョン
image.png

JavaScriptでAPI実行

Fetch APIを使うと素のJavaScriptでも簡単にAPIを実行できることを知った。

具体的には、例えば以下のようなコードを実行するとurlに指定したAPIの実行結果を取得/加工/表示することができる。

function get(url) {
  fetch(url)
  .then(function(response) {
    return response.text();  // ここのtextをjsonやblobに変えると取得形式が変わる
  })
  .then(function(text) {  // 引数指定すると↑でreturnしたオブジェクトが入る(thenでチェーンしていける)
    // 加工や表示などの処理
  });
}

簡単なHTMLと加工部分をまとめて一緒に書くと以下のような感じ。
ボタンを押すとテキストボックスに入力されたテキストがPostされ、Post後にGetの実行結果が表示される。
PostとGetの具体的な処理は下記のFlaskのコードhoge.pyを参照

Postで送るときのパラメータの指定方法で困ったが、以下のstackoverflowに書いてあった方法で解決した。
How do I post form data with fetch api?

index.html
<html>
  <head>
    <title>APIテスト</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <script>
    function get_func(url) {
      fetch(url)
      .then(function(response) {
        return response.text();
      })
      .then(function(text) {
        let view = document.getElementById("view")
        view.textContent = ""
        // 取得テキストを一行ごとにループ(ただ改行して表示しているだけ)
        text.split("\n").forEach((value) => {
          view.insertAdjacentHTML('beforeend', value);
          view.insertAdjacentHTML('beforeend', "<br>");
        })
      });
    }

    function post_func(url) {
      // Postで送るパラメータを作成
      let formData = new FormData();
      formData.append('param', document.getElementById('post_param').value);
      fetch(url, {
        method: 'POST',  // methodを指定しないとGETになる
        body: formData,  // Postで送るパラメータを指定
      })
      .then(function() {  // Postした後に結果をGetする(コールバックなのでPostが実行完了してから実行される)
        get_func('APIのGet用URL');  // 今回は「http://192.168.1.100:4000/get」
      });
      // ここにget_funcを書くとPostとGetがほぼ同時に行われてしまい、Post結果をGetできない
      // get_func('APIのGet用URL');
    }
    </script>
  </head>
  <body>
    <input type="text" id="post_param" size=20 value="hoge">
    <input type="submit" value="送信" onClick="post_func('APIのPost用URL')">  <!-- 今回は「http://192.168.1.100:4000/post」-->
    <div id="view"></div>
  </body>
</html>

FlaskでAPI作成

Flask(フラスク)は、プログラミング言語Python用の、軽量なウェブアプリケーションフレームワークである。
出展:Wikipedia「https://ja.wikipedia.org/wiki/Flask」

基本的な使い方はググればたくさん出てくるので省略

なお、ローカルや別ドメインからAPIを実行しようとするとクロスドメイン制約で拒否られる。
Flask-CORSを入れてCORS(Cross Origin Resource Sharing)を有効にしておくとこれを防げる。

hoge.py
from flask import Flask, jsonify, abort, make_response, render_template, request
from flask_cors import CORS

api = Flask(__name__)
CORS(api)  # CORS有効化

@api.route('/get', methods=['GET'])  # Getだけ受け付ける
def get():  # 関数名は重複していなければなんでもよい
    result = ""
    # ローカルのファイルを全部読み込んで返すだけ
    with open("./datafile", mode='r') as f:
        result += f.read()
    return result

@api.route('/post', methods=['POST'])  # Postだけ受け付ける
def post():
    result = request.form["param"]  # Postで送ったときのパラメータの名前を指定する
    # パラメータをローカルのファイルに書き込むだけ
    with open("./datafile", mode='a') as f:
        f.write(result + "\n")
    return make_response(result)

# 4000番ポートでWebサーバを起動する
if __name__ == '__main__':
    api.run(host='0.0.0.0', port=4000, debug=True)

これを実行するとWebサーバが起動し、http://サーバIP:4000/getなどのURLで実行することが可能になる。

前述したJavaScriptと合わせると、簡易的なフローは以下になる。

  1. post_paramに文字を入力して送信ボタンを押下(クライアント側処理
  2. JavaScriptで定義したpost_func関数が実行される(クライアント側処理
  3. http://サーバIP:4000/postが呼ばれる(クライアントからサーバへのRequest
  4. Flaskで定義したpost関数が実行される(サーバ側処理
  5. post関数実行結果が返ってくる(サーバからクライアントへのResponse
  6. Post処理実行完了後にJavaScriptで定義したget_func関数が実行される(クライアント側処理
  7. http://サーバIP:4000/getが呼ばれる(クライアントからサーバへのRequest
  8. Flaskで定義したget関数が実行される(サーバ側処理
  9. get関数実行結果が返ってくる(サーバからクライアントへのResponse
  10. get_func関数内でget関数の実行結果を加工して表示する(クライアント側処理

これでAPIを自作して呼び出すことができるようになった。

Dockerの諸々のファイル紹介

特に説明はしません。

Dockerfile
FROM python:3-alpine

RUN mkdir /test
WORKDIR /test
COPY requirements.txt /test/

RUN pip install -r requirements.txt

CMD ["python3","code/hoge.py"]
requirements.txt
flask
flask-cors
docker-compose.yml
version: '3'

services:
  test:
    build: ./test/build/
    tty: true
    restart: always
    ports: 
      - 4000:4000
    volumes:
      - /etc/localtime:/etc/localtime:ro  # ホストと時刻動機
      - ./test/data/:/test
ディレクトリ構成
.
├── docker-compose.yml
└── test
    ├── build
    │   ├── Dockerfile
    │   └── requirements.txt
    └── data
        ├── code
        │   ├── hoge.py    # Flaskのコード
        │   ├── static     # サーバ内にHTMLファイルなど置きたかったらこのあたりを使う
        │   └── templates  # サーバ内にHTMLファイルなど置きたかったらこのあたりを使う
        └── datafile       # Post時に自動生成されるファイル

なお、冒頭の画像ではtemplatesindex.htmlを配置してhoge.py/アクセス時にindex.htmlを表示するコードを書いてアクセスしていますが、
index.htmlをローカルに置いてアクセスしても動きます。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away