FalconでAPIの前処理・後処理を実装する
Flaskに比べて高速なAPIを実装できるFalconを利用した場合、共通的な前処理・後処理は2つの実装方法がある。
認証処理やデータベースの接続処理の共通化の調査結果をメモとしてまとめておく。
-
Hooks
API単位にデコレータをセットし、前処理・後処理はそれぞれのHooksで設定する。API単位に細かく設定を変更した場合に向く。 -
Middleware
ソースのAPI全体にミドルウェアとして設定する。前処理・後処理のほかメインルーチンも設定できる。
Hooks ... API単位に設定
API単位に前処理・後処理をHooks(デコレータ)で設定する。Flaskでいうbefore_request、after_requestメソッドに相当する。
[処理の実装]
def before_hook(req, resp, resource, params):
# 前処理を実装する
def after_hook(req, resp, resource):
# 後処理を実装する
class TestResource(object):
@falcon.before(before_hook)
@falcon.after(after_hook)
def on_post(self, req, res):
# POSTメソッドの処理
app = falcon.API(middleware=[])
app.add_route("/", TestResource())
[処理の中断]
処理途中で例外を発生させる場合はraiseを利用する
たとえば前処理を途中で打ち切る場合、以下のようなコードを記載することで後続処理は実行されない。
def before_hook(req, resp, resource, params):
raise falcon.HTTPBadRequest('Bad request')
Middleware ... ソース内のAPI全体に設定
前処理、メインルーチン、後処理をそれぞれのメソッドで作成する。
[処理の実装]
class TestMiddleware(object):
# 前処理
def process_request(self, req, resp):
# 前処理を実装する
# メインルーチン
def prrocess_resource(self, req, resp, resource, params):
# メインルーチンを実装する
# 後処理
def process_response(self, req, resp, resource, req_succeeded):
# 後処理を実装する
[Middlewareの登録]
falconの初期化時にMiddlewareを組み込む。パラメタは配列形式なので複数指定できる。Falconからは標準機能としてCORSMiddlewareが提供されている。
app = falcon.API(middleware=[middleware.TestMiddleware()])
[処理の中断]
resp.complete、resp.statusを設定する。
たとえば前処理を途中で打ち切る場合、以下のようなコードを記載することで後続処理は実行されない。
class TestMiddleware(object):
# 前処理
def process_request(self, req, resp):
resp.complete = True
resp.status = falcon.HTTP_400
その他
Flaskでのグローバル機能(g変数)に近い機能は、将来実装予定だと思われる。
このやり方が正しいかは不明だが、たとえば前処理でreqに独自のメンバ変数を格納することで、後続処理でその変数を参照した動作を作成できる。
[例]
- 前処理でSQLiteのコネクションを取得しreq.curにcursorオブジェクトを格納
- API内部でreq.curを利用してSQLite3にINSERT
- 後処理でcommit / rollback
# -*- coding:utf-8 -*-
import json
import falcon
import sqlite3
# SQLite3データベースの準備
conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.execute('create table test(data text)')
conn.commit()
cur.close()
# 前処理のHooks
def before_hook(req, resp, resource, params):
# SQLite3データベースのcursorオブジェクトを作成
req.cur = conn.cursor()
# 後処理のHooks
def after_hook(req, resp, resource):
# statusコードによりcommitかrollbackを実行
if resp.status != falcon.HTTP_200:
conn.rollback()
else:
conn.commit()
req.cur.close()
class TestResource(object):
@falcon.before(before_hook)
@falcon.after(after_hook)
def on_post(self, req, res):
# POSTメソッドの実装
data = json.load(req.bounded_stream)
req.cur.execute("insert into test values(?)", (str(data),))
msg = {"message": "POST OK"}
#res.status = falcon.HTTP_400
res.body = json.dumps(msg)
app = falcon.API(middleware=[])
app.add_route("/", TestResource())
if __name__ == "__main__":
from wsgiref import simple_server
httpd = simple_server.make_server("0.0.0.0", 8000, app)
httpd.serve_forever()