TL; DR
FlaskでファイルアップロードAPIを実装し、REST Clientで叩きました。
はじめに
この記事ではFlaskで簡単なGETのAPIを実装するところから、
実際にファイルをPOSTするAPIを実装するところまでを、3 Stepsで記載しています。
Flaskにあまり馴染みが無い方も超簡単にファイルアップロードAPIできるようになれます(きっと)。
環境
Python 3.7
Flask 1.1
VSCode 1.38
ディレクトリ構成
/
├ data/
├ __init__.py
├ api.py
├ call.http
├ face.png
└ requirements.txt
1. APIの実装(JSONのGET)
FlaskでのAPIの実装マジで簡単。やったぜ。
1-1. 実装
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/item', methods=['GET'])
def get_item():
item = {"item_name": 'hogehoge'}
return jsonify(item)
if __name__ == '__main__':
app.run(debug=True)
これだけ。やったぜ。
1-2. 叩く
さっそく試しに叩いてみよう!
......あれよく考えたらあんまり叩き方知らないな?(情弱)
1-2-0. REST Client
ググってみたら次の記事を見つけました。
REST Client allows you to send HTTP request and view the response in Visual Studio Code directly.
VSCode上で HTTP リクエストができるとな?便利そう。
早速 REST Client を入れて叩いてみました。
1-2-1. Flask起動
$ python api.py
* Serving Flask app "api" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 111-858-152
1-2-2. リクエスト
### GET item
GET http://localhost:5000/api/item
Cmd + Option + R で送信! (Windows は Ctl + Alt + R)
1-2-3. 結果
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 30
Server: Werkzeug/0.16.0 Python/3.7.3
Date: Thu, 03 Oct 2019 16:54:12 GMT
{
"item_name": "hogehoge"
}
やったぜ。
2. API 実装(ユーザー情報のPOST)
GET はうまくいったので POST も試してみよう。
ということでまずはFlaskでAPIを実装。
2-1. 実装
from flask import Flask, jsonify, request
import bleach
app = Flask(__name__)
TOKEN = 'YOUR_TOKEN'
@app.route('/api/v1/user', methods=['POST'])
def add_user():
# check token
header = request.headers.get('Authorization', None)
_, token = header.split()
if token != TOKEN:
return jsonify({'Forbidden': 'Access is denied'}), 403
# method check
if request.method != 'POST':
return jsonify({'Method Not Allowed': 'Method is invalid.'}), 405
# create new user
new_user = {}
for key in request.form.keys():
new_user[key] = bleach.clean(request.form.get(key))
return jsonify(new_user)
if __name__ == '__main__':
app.run(debug=True)
これだけ。やったぜ。
ほんとはDBと繋いで new_user を登録したりしたんですけど、今回は割愛。
Flask でリクエストを受け取る時は request を使用します。
今回は Content-Type は multipart/form-data で送信するつもりなので、request.form を使用しています。
他にもパラメータを受け取る場合は request.args 、json を受け取る時は request.json を使用します。
2-2. 叩く
2-2-1. Flask起動
さっきと同じようにapi.pyを動かしておきましょう。
2-2-2. リクエスト
#######
### POST user info
POST http://localhost:5000/api/v1/user
Authorization: Bearer YOUR_TOKEN
Content-Type: multipart/form-data; boundary=HOGEHOGEBOUNDARY
--HOGEHOGEBOUNDARY
Content-Disposition: form-data; name=user_name
SAKAMOTO RYOMA
--HOGEHOGEBOUNDARY
Content-Disposition: form-data; name=user_email
ryoma.sakamoto@...
--HOGEHOGEBOUNDARY--
なるほど multipart 形式でPOSTする場合は boundary っていうのを設定するんですね。(情弱)
送信する要素ごとに --{your boundary} を記述して、
要素の最後は --{your boundary}-- で締めくくるんですねぇ。
このへんはRFC7578で規定されていますのでご確認ください。
Content-Disposition の name が記述されていますが、これはHTMLの
<form>
<input name="user_name">
</form>
と同じです。
2-2-3. 結果
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 98
Server: Werkzeug/0.16.0 Python/3.7.3
Date: Thu, 03 Oct 2019 17:24:57 GMT
{
"user_email": "ryoma.sakamoto@...",
"user_name": "SAKAMOTO RYOMA"
}
やったぜ。無事POSTできましたね。
3. APIの実装(アップロードファイルのPOST)
さて、ようやく本題です。
ユーザー情報送るなら顔写真とかも送れた方が良いよね?そうでもないですか?
ファイルアップロードのAPIって実装めんどくさいよなぁと思ってましたが超簡単でした!
そうFlaskとREST Clientならね。
3-1. 実装
(10月7日check token部分修正しました。)
from flask import Flask, jsonify, request
import bleach
app = Flask(__name__)
TOKEN = 'YOUR_TOKEN'
@app.route('/api/v1/user', methods=['POST'])
def add_user():
# check token
header = request.headers.get('Authorization', None)
if header is not None:
_, token = header.split()
if token != TOKEN:
return jsonify({'Forbidden': 'Access is denied'}), 403
else:
return jsonify({'Forbidden': 'Access is denied'}), 403
# method check
if request.method != 'POST':
return jsonify({'Method Not Allowed': 'Method is invalid.'}), 405
# create new user
new_user = {}
for key in request.form.keys():
new_user[key] = bleach.clean(request.form.get(key))
### このへん追加
# save uploaded file into data folder
for file in request.files:
if file is None:
break
upload_file = request.files.get(file)
upload_path = 'data/%s' % upload_file.filename
upload_file.save(upload_path)
new_user[file] = upload_file.filename
return jsonify(new_user)
if __name__ == '__main__':
app.run(debug=True)
これだけ。やったぜ。
Flaskでファイルを受け取る場合、 request.files の中に入っていきます。
3-2. 叩く
3-2-1. Flask起動
さっきと同じようにapi.pyを動かしておきましょう。
また、事前に顔写真 face.png を api.py と同じディレクトリに置いておきましょう。
3-2-2. リクエスト
#######
### OK
POST http://localhost:5000/api/v1/user
Authorization: Bearer YOUR_TOKEN
Content-Type: multipart/form-data; boundary=HOGEHOGEBOUNDARY
--HOGEHOGEBOUNDARY
Content-Disposition: form-data; name=user_name
SAKAMOTO RYOMA
--HOGEHOGEBOUNDARY
Content-Disposition: form-data; name=user_email
ryoma.sakamoto@...
--HOGEHOGEBOUNDARY
Content-Disposition: form-data; name="file"; filename="face.png"
Content-Type: application/octet-stream
< ./face.png
--HOGEHOGEBOUNDARY--
3-2-3. 結果
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 98
Server: Werkzeug/0.16.0 Python/3.7.3
Date: Thu, 03 Oct 2019 18:01:26 GMT
{
"file": "face.png",
"user_email": "ryoma.sakamoto@...",
"user_name": "SAKAMOTO RYOMA"
}
dataフォルダをのぞいてみるとちゃんと face.png が保存されていました!
くぅ疲、これにて目的達成です。やったぜ。
感想
ほんとFlask はAPI さくっと書けて優秀だなぁと思います。
情弱の弁解ですが、普段はAPIはJavascriptのAjaxだったり、Pythonのrequestsだったりで叩いているのですが、
curlについて調べているときにREST Clientについて知ったので使ってみた次第です。