Django, FastAPI 등에서 직접 HTTP Request object의 정보를 가져오기 위해서는
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/items/{item_id}")
def read_root(item_id: str, request: Request):
client_host = request.client.host
return {"client_host": client_host, "item_id": item_id}
from django.http import HttpResponse
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
output = ', '.join([q.question_text for q in latest_question_list])
return HttpResponse(output)
일반적으로 위와 같이 view layer ( Controller ) 에서 endpoint 별로 request 형태로 받아서 사용하게 된다. 따라서 request에 대한 조작이 필요하다면 매개변수에 항상 넣어줘야하는 불편함이 있다.
이에 Flask 는
from flask import request
def index():
if request.method == 'OPTIONS':
# custom options handling here
...
return 'Hello World!'
request 라는 전역변수를 사용하게 된다. ( 뒤에 서술하겠지만 완전한 전역변수는 아니고 werkzeug 에 있는 구현체인 LocalStack 타입의 변수이다.
따라서 view func 마다 request를 써주거나 할 필요가 없어진다.
그렇다면 이러한 전역변수 관리는 어떻게 이루어지길래 thread, process, coroutine 에 따라 safe하게 관리가 될 수 있을까?
Flask 에 request가 들어오게 되면 global 하게 Application Context, Request Context가 형성되게 된다.
두 context모두 전반적으로 두루 사용되는 데이터를 담고 있게 되는데 그 성질에 따라서 두 개의 context로 나눠지게 된다.
application context 는 주로 logger, db configuration 과 같이 시스템 전반적으로 공유되는 data 정보를 담고 있게 된다.
실제 사용자 ( 개발자 )는 application context의 데이터에 접근하기 위해서 proxy 객체를 이용하게 되는데 그것이 바로 우리가 사용하는 current_app, g 이다.
from werkzeug.local import LocalProxy
from werkzeug.local import LocalStack
def _find_app():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError(_app_ctx_err_msg)
return top.app
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
g = LocalProxy(partial(_lookup_app_object, "g"))
실제 코드를 보면 위와 같이 werkzeug(밸저그)에서 가져온 LocalStack, LocalProxy를 이용함을 볼 수 있다.
request context는 url, http method, request header etc... 같은 request level의 정보를 담고있다.
from functools import partial
from werkzeug.local import LocalProxy
from werkzeug.local import LocalStack
def _lookup_req_object(name):
top = _request_ctx_stack.top
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name)
_request_ctx_stack = LocalStack()
request = LocalProxy(partial(_lookup_req_object, "request"))
session = LocalProxy(partial(_lookup_req_object, "session"))
view function 내에서 사용을 하거나 혹은 아래와 같이 임의로 app_context, request_context를 만들어서 접근할 수 있도록 context를 만들어줘야한다.
from app import app
from flask import request, current_app
with app.app_context():
current_app.config
with app.test_request_context():
request.method
with app.request_context(environ): # <-- WSGI environ 정보가 들어가 있다.
request.method
request.path