FlaskでREST APIを作ってみる

簡単のため、DBは使わずにPythonディクショナリのCRUDを作ります。

Flaskに追加して使うFlask-RESTfulというパッケージもありますが、 今回の範囲では、Flaskだけで実装するのと大きな差を感じなかったので使っていません。

バージョンなど

  • Python
    3.7
  • Flask
    1.0.2

やりたいこと

  • REST APIを作る
  • CRUD処理ができるようにする(Pythonディクショナリで)

準備

Flaskをインストールしておきます。( pip --version でPython2系が出る場合は pippip3 に読み替えて下さい。)

pip install Flask

Hello, world

Dockerで動かすにはここを参照。

とりあえず基本的な使い方を見てみましょう。
/hello にGETでアクセスするとJSONで {"message": "Hello, world"} を返すようにしてみます。

コード

以下のように書いたファイルを、たとえば hello.py とします。

from flask import Flask, jsonify
app = Flask(__name__)

@app.route('/hello')
def hello_world():
return jsonify({'message': 'Hello, world'})

実行

実行は flask run です。
またこのとき、上の hello.py をflaskに読み込ませるため、環境変数 FLASK_APPhello.py を指定します。

$ FLASK_APP=hello.py flask run

なお、Dockerの例では --host オプションをつけていますが、普通に実行する場合には不要です。Dockerコンテナ内で実行する場合はホストからのアクセスを受け付けさせるために必要です。 --host=0.0.0.0 とすることで、どこからのアクセスも受け付けるようになります。

確認

curlでレスポンスを確認してみましょう。ポート番号はデフォルトでは5000です。
Content-Typeヘッダがちゃんと application/json になっていることがわかります。

$ curl localhost:5000/hello
{"message":"Hello, world"}
$ curl --head localhost:5000/hello
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 27
Server: Werkzeug/0.14.1 Python/3.7.1
Date: Sun, 09 Dec 2018 03:03:20 GMT

メモ

基本事項をまとめておきます。

ルーティング

Dockerで動かすにはここを参照。

ドキュメントはここここにあります。

メソッドの上に @app.route('/foo/bar'), methods=['GET', 'POST'] のように書くことで設定できます。 methods は省略するとGETになります。
変数(baz)を表現したいときはエンドポイントを '/foo/bar/<baz>' のように書きます。型も指定できます。

from flask import Flask
app = Flask(__name__)

@app.route('/foo/bar')
def get_foobar():
return 'you got\n'
@app.route('/foo/bar/<int:baz>', methods=['POST'])
def post_foobar(baz):
return 'you posted {}\n'.format(baz)

使用例は以下です。

$ curl localhost:5000/foo/bar
you got
$ curl -X POST localhost:5000/foo/bar/42
you posted 42

JSONの受け渡し

Dockerで動かすにはここを参照。

ドキュメントはここにあります。

from flask import Flask, request, jsonify
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False # JSONでの日本語文字化け対策

@app.route('/', methods=['POST'])
def post_json():
json = request.get_json() # POSTされたJSONを取得
return jsonify(json) # JSONをレスポンス

@app.route('/', methods=['GET'])
def get_json_from_dictionary():
dic = {
'foo': 'bar',
'ほげ': 'ふが'
}
return jsonify(dic) # JSONをレスポンス

使用例は以下です。

$ curl localhost:5000/
{"foo":"bar","ほげ":"ふが"}
$ curl -X POST -H 'Accept:application/json' -H 'Content-Type:application/json' -d '{"data":"hoge"}' localhost:5000/
{"data":"hoge"}
  • レスポンス
    jsonify を使います。Pythonディクショナリなどに対して使うと application/json でのレスポンスが返ります。
  • リクエスト
    request.json() でPOSTされたJSONを取得できます。
  • 日本語の文字化け問題
    jsonifyで日本語を出力すると文字化けするようです。
    下の2行目の設定で解決できます。
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False

CRUD

Dockerで動かすにはここを参照。

以上をふまえて、PythonディクショナリでCRUDを作ると、以下のようになります。

from flask import Flask, request, json, jsonify
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False
tasks = {
'1': 'スーパーで買い物をする',
'2': 'Netflixを見る'
}

@app.route('/tasks', methods=['GET'])
def list_all_tasks():
json = {
'message': tasks
}
return jsonify(json)

@app.route('/tasks/<int:taskid>', methods=['GET'])
def show_task(taskid):
taskid = str(taskid)
json = {
'message': tasks[taskid]
}
return jsonify(json)

@app.route('/tasks/<int:taskid>', methods=['DELETE'])
def delete_task(taskid):
taskid = str(taskid)
if taskid in tasks:
del tasks[taskid]
msg = 'Task {} deleted'.format(taskid)
else:
msg = '{0} is not in tasks.'.format(taskid)
json = {
'message': msg
}
return jsonify(json)

@app.route('/tasks', methods=['POST'])
def create_task():
taskid = str(int(max(tasks.keys())) + 1)
posted = request.get_json()
if 'task' in posted:
tasks[taskid] = posted['task']
msg = 'New task created'
else:
msg = 'No task created'
json = {
'message': msg
}
return jsonify(json)

@app.route('/tasks/<int:taskid>', methods=['PUT'])
def update_task(taskid):
taskid = str(taskid)
posted = request.get_json()
if 'task' in posted and taskid in tasks:
tasks[taskid] = posted['task']
msg = 'Task {} updated'.format(taskid)
else:
msg = 'No task updated'
json = {
'message': msg
}
return jsonify(json)

使用例は以下です。

$ curl localhost:5000/tasks
{"message":{"1":"スーパーで買い物をする","2":"Netflixを見る"}}
$ curl localhost:5000/tasks/1
{"message":"スーパーで買い物をする"}
$ curl -X POST -H 'Content-Type:application/json' -d '{"task": "ジムに行く"}' localhost:5000/tasks
{"message":"New task created"}
$ curl -X DELETE localhost:5000/tasks/2
{"message":"Task 2 deleted"}
$ curl localhost:5000/tasks
{"message":{"1":"スーパーで買い物をする","3":"ジムに行く"}}
$ curl -X PUT -H 'Accept:application/json' -H 'Content-Type:application/json' -d '{"task": "映画館に行く"}' localhost:5000/tasks/3
{"message":"Task 3 updated"}
$ curl localhost:5000/tasks
{"message":{"1":"スーパーで買い物をする","3":"映画館に行く"}}

終わりに

日本語の文字化けなど詰まるところもありましたが、基本的にはコツをつかめば楽に書ける印象です。手っ取り早くAPIを作るには向いているのではないでしょうか。

実用的なものにするにはここからさらに進めて、エラー処理、ロギング、nginxとの連携などをしっかりやることが必要です。

参考

json

ルーティング

日本語の文字化け問題