FlaskでREST APIを作ってみる

sankaku
sankaku
Dec 25, 2018 · 10 min read

簡単のため、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 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

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'] = 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との連携などをしっかりやることが必要です。

参考

Nyle Engineering Blog

ナイル株式会社のエンジニアブログです。ナイル社内で利用している技術の情報や、社内の様子などを発信しています。

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store