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系が出る場合は pip
を pip3
に読み替えて下さい。)
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_APP
に hello.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 Flaskapp = 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, jsonifyapp = 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, jsonifyapp = Flask(__name__)
app.config['JSON_AS_ASCII'] = Falsetasks = {
'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との連携などをしっかりやることが必要です。