LoginSignup
0
0

More than 3 years have passed since last update.

FalconでAPIの前処理・後処理をHooksとMiddlewareで実装

Posted at

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全体に設定

前処理、メインルーチン、後処理をそれぞれのメソッドで作成する。

[処理の実装]

middleware.py
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に独自のメンバ変数を格納することで、後続処理でその変数を参照した動作を作成できる。

[例]

  1. 前処理でSQLiteのコネクションを取得しreq.curにcursorオブジェクトを格納
  2. API内部でreq.curを利用してSQLite3にINSERT
  3. 後処理で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()

参考ページ

Falcon
Hooks - Falcon
Middleware - Falcon

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0