Pythonの軽量WebフレームワークBottleのソースを読む リクエスト・レスポンス編
リクエスト受付からレスポンスまで
前回、サーバの立ち上げ時にルーティングが読み込まれるところまでを確認したので今回はリクエスト受付からレスポンスを返すまでを見てみる。まずリクエストが来るとwsgiref/handlers.pyのrunが呼ばれ、Bottleの__call__が呼び出される
wsgiref/handlers.py
self.setup_environ()
# リクエストを処理しレスポンスを取得する
self.result = application(self.environ, self.start_response)
# レスポンスを返す
self.finish_response()
bottle.py
def __call__(self, environ, start_response):
""" Each instance of :class:'Bottle' is a WSGI application. """
return self.wsgi(environ, start_response)
リクエストを処理する際に、LocalRequestのbindを呼び出しBaseRequestのコンストラクタにてenvironとLocalRequestをセットする。これでBaseRequestのラッパーからパラメータを取得可能になる。レスポンスも同様にLocalResponseのbindを呼び出す。
try:
out = None
environ['bottle.app'] = self
request.bind(environ)
response.bind()
try:
self.trigger_hook('before_request')
except HTTPResponse:
return _e()
# ここでリクエストの主処理を実行する
out = _inner_handle()
return out;
finally:
if isinstance(out, HTTPResponse):
out.apply(response)
self.trigger_hook('after_request')
BaseRequestのコンストラクタ
self.environ = {} if environ is None else environ
self.environ['bottle.request'] = self
リクエスト情報は以下のように取得可能になる
def index():
request.forms.get('test')
return 'TOP'
LocalRequestは以下の通りマルチスレッドに対応している
#: A thread-safe instance of :class:`LocalRequest`. If accessed from within a#: request callback, this instance always refers to the *current* request#: (even on a multi-threaded server).
hookポイントで以下のようにbefore_requestとafter_requestが利用可能
@hook('before_request')
def before_request():
request.session = 'session'
@hook('after_request')
def after_request():
print 'test'
handleのinner_handle()にてrouteのactionを呼び出しresponseを取得する
def _inner_handle():
# Maybe pass variables as locals for better performance?
try:
route, args = self.router.match(environ)
environ['route.handle'] = route
environ['bottle.route'] = route
environ['route.url_args'] = args
return route.call(**args)
リダイレクトは以下のメソッドが用意されている
def redirect(url, code=None):
""" Aborts execution and causes a 303 or 302 redirect, depending on
the HTTP protocol version. """
if not code:
code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302
res = response.copy(cls=HTTPResponse)
res.status = code
res.body = ""
res.set_header('Location', urljoin(request.url, url))
raise res