CLOVER🍀

That was when it all began.

Flask RESTfulを試す

これは、なにをしたくて書いたもの?

Pythonで、簡単にREST APIを作れるフレームワークを知りたいなと思いまして。

PythonでのWebフレームワークといえば、DjangoとFlaskが有名みたいですが、Flask RESTfulが入りやすそうだったので、こちらを
試してみることにしました。

Flask-RESTful — Flask-RESTful 0.3.7 documentation

環境

今回の環境は、こちら。

$ python3 -V
Python 3.6.8

Flask RESTfulのインストール

まずは、インストール。

Installation — Flask-RESTful 0.3.6 documentation

$ pip3 install flask-restful

インストールされたFlask RESTfulは0.3.7。

$ pip3 freeze
aniso8601==7.0.0
Click==7.0
Flask==1.1.1
Flask-RESTful==0.3.7
itsdangerous==1.1.0
Jinja2==2.10.1
MarkupSafe==1.1.1
pkg-resources==0.0.0
pytz==2019.2
six==1.12.0
Werkzeug==0.15.5

なのですが、参照するドキュメントのバージョンは固定したいので、リンク上は0.3.6のドキュメントを書いていくことにします…。

Hello World

こちらを見ながら、Hello World。

Quickstart — Flask-RESTful 0.3.6 documentation

hello_flask.py

from flask import Flask
from flask_restful import Api, Resource

app = Flask(__name__)
api = Api(app)

class HelloWorld(Resource):
    def get(self):
        return { 'message': 'Hello World' }

api.add_resource(HelloWorld, '/')

if __name__ == '__main__':
    app.run(debug = True)

FlaskおよびApiのインスタンスを作成して

app = Flask(__name__)
api = Api(app)

これに、Resourceクラスを登録するようですね。第2引数が、ルーティングのパスのようです。

api.add_resource(HelloWorld, '/')

Api.add_resource

作成するResourceのサブクラスは、HTTPメソッドに応じてget、postメソッドなどを作成するようです。

class HelloWorld(Resource):
    def get(self):
        return { 'message': 'Hello World' }

Resource

起動部分。

if __name__ == '__main__':
    app.run(debug = True)

この「debug = True」は、以下あたりを見ればよさそうです。コードを修正した時の、自動リロードもやってくれるみたいです。

Quickstart / Debug Mode

Werkzeug — Werkzeug Documentation (0.15.x)

今回は、あまり追わず。

では、起動してみます。

$ python3 hello_flask.py 
 * Serving Flask app "hello_flask" (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: 980-333-426

確認。

$ curl localhost:5000
{
    "message": "Hello World"
}

動きましたね。

URLパスに、変数を設定する

URLパスに、変数を入れることもできるようです。

Resourceful Routing

ちょっと試してみましょう。

先ほどのファイルに、追加でResourceを足してみます。

class VariableRouting(Resource):
    def get(self, id):
        return { 'id': id }
   
api.add_resource(HelloWorld, '/')
api.add_resource(VariableRouting, '/var/<string:id>')

「<型:名前>」でルーティングの設定時にパス指定をして、Resourceのメソッドで受け取るようです。

確認。

$ curl localhost:5000/var/123456
{
    "id": "123456"
}

QueryStringやHTTPボディを扱ってみる

QueryStringやHTTPボディを扱うには、FlaskのAPIを使えばいいみたいです。

Accessing Request Data

こんな感じで、Flaskのrequestを使います。
server.py

from flask import Flask, request
from flask_restful import Api, Resource, reqparse

app = Flask(__name__)
api = Api(app)
parser = reqparse.RequestParser()

class QueryResource(Resource):
    def get(self):
        query_string = request.query_string
        param = request.args.get('param')
        
        return {
            'query_string':  query_string.decode('utf-8'),
            'param': param
        }

class PostResource(Resource):
    def post(self):
        json = request.get_json(force = True)
        return { 'json_request': json }

api.add_resource(QueryResource, '/get')
api.add_resource(PostResource, '/post')

if __name__ == '__main__':
    app.run(debug = True)

QueryStringを扱う場合。

    def get(self):
        query_string = request.query_string
        param = request.args.get('param')
        
        return {
            'query_string':  query_string.decode('utf-8'),
            'param': param
        }

JSONデータを扱う場合。

    def post(self):
        json = request.get_json(force = True)
        return { 'json_request': json }

request.get_jsonの「force = True」を付けない場合は、Content-Typeにapplication/jsonを指定しないとJSONを認識してくれません。

確認。

$ curl localhost:5000/get?param=value
{
    "query_string": "param=value",
    "param": "value"
}


$ curl localhost:5000/post -d '{"name":"value"}'
{
    "json_request": {
        "name": "value"
    }
}

OKそうです。

WSGIサーバーで動かしてみる

最後に、WSGIサーバーで動かしてみましょう。

今回は、Gunicornを使うことにします。

インストール。

$ pip3 install gunicorn

バージョン。

gunicorn==19.9.0

起動方法としては、こんな感じで。

$ gunicorn hello_flask:app

拡張子なしのファイル名の後に指定するのは、Flaskインスタンスを格納した変数名みたいです。

app = Flask(__name__)

起動。

$ gunicorn hello_flask:app
[2019-08-14 23:53:35 +0900] [26331] [INFO] Starting gunicorn 19.9.0
[2019-08-14 23:53:35 +0900] [26331] [INFO] Listening at: http://127.0.0.1:8000 (26331)
[2019-08-14 23:53:35 +0900] [26331] [INFO] Using worker: sync
[2019-08-14 23:53:35 +0900] [26334] [INFO] Booting worker with pid: 26334

確認。

$ curl -i localhost:8000
HTTP/1.1 200 OK
Server: gunicorn/19.9.0
Date: Wed, 14 Aug 2019 14:53:56 GMT
Connection: close
Content-Type: application/json
Content-Length: 27

{"message": "Hello World"}

OKそうです。

これで、ちょっとしたREST APIなら書けそうですね。

ところで書き始めた後に気づいたのですが、素のFlaskでも実はけっこう書けるのでは?と気づきました…。

Routing

About Responses

APIs with JSON

Flask RESTfulの場合は、エンドポイントを関数ではなくResourceクラスとして表現できることと、ルーティングの設定を
まとめられるとかがポイントでしょうかね。

まあ、いいや…。