Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
10
Help us understand the problem. What is going on with this article?
@tesstesstex

FlaskでファイルアップロードAPIを実装し REST Clientで叩いた話

More than 1 year has passed since last update.

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. 実装

api.py
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. リクエスト

call.http
### GET item
GET http://localhost:5000/api/item

Cmd + Option + R で送信! (Windows は Ctl + Alt + R)

1-2-3. 結果

response
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. 実装

api.py
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. リクエスト

call.http
#######
### 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の

html
<form>
 <input name="user_name">
</form>

と同じです。

2-2-3. 結果

response
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部分修正しました。)

api.py
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. リクエスト

call.http
#######
### 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. 結果

response
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 が保存されていました!
スクリーンショット 2019-10-04 3.03.35.png
くぅ疲、これにて目的達成です。やったぜ。

感想

ほんとFlask はAPI さくっと書けて優秀だなぁと思います。
情弱の弁解ですが、普段はAPIはJavascriptのAjaxだったり、Pythonのrequestsだったりで叩いているのですが、
curlについて調べているときにREST Clientについて知ったので使ってみた次第です。

参考

Qiita

Auth0

10
Help us understand the problem. What is going on with this article?
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
tesstesstex
JavaScript/Python

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
10
Help us understand the problem. What is going on with this article?