Django의 요청 / 응답 처리에 대한 후크 프레임 워크로써 Django 상의 모든 API 입출력을 전역적으로 변경하기 위한 플러그인 시스템이다.
Official Docs 에서 "Django의 입력 또는 출력을 전역적으로 변경하기 위한 가볍고 낮은 수준의 "플러그인" 시스템입니다." 라고 언급힌다.
settings.py
에 있는 많은 middleware를 볼 수 있다. MIDDLEWARE = [
# default
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
...
]
MIDDLEWARE
라는 list
의 0번째 index 부터 HttpRequest, HttpResponse가 loop 돌 듯이 거쳐간다. "순차적" 이라는 것이다. HttpRequest Object 관점에서 보면, SecurityMiddleware
를 시작으로, 각 미들웨어에서 process_request(), process_view() 메서드를 실행하면서 우리가 정의한 view
까지 도달하는 것이다.
그리고 사실 middleware끼리 디펜던시가 있는 것도 있어서 순서가 중요하다. 예를 들어 AuthenticationMiddleware
는 인증된 사용자를 세션에 저장하기 때문에 SessionMiddleware
이후에 실행되어야 한다. 이런 기본 미들웨어의 순서에 대한 고찰은 여기서 확인할 수 있다.
원리는 아주 간단하다. class또는 function based로, django에서 미리 만들어둔 "미들 웨어 훅" 을 정의하고 사용하면 된다.
HttpRequest -> HttpResponse
이 처리 구간에서 time library의 process_time_ns
함수를 활용해서 응답 헤더에 추가하는 미들웨어를 만들어 보자.
Factory method pattern 은
Template Method의 생성 패턴 버전
이라고 도 한다. 특정 역할을 가진 객체를 생산하는 형태로 짜여진 디자인 패턴이다.
부모(상위) 클래스가 알지 못하는 구체 클래스 생성 패턴이며, 자식(하위) 클래스가 스스로 어떤 객체를 생성할지 결정한다.
custom_middleware.py
를 아래와 같이 만들고, settings.py에 추가하자.# custom middlewawre - factory method pattern
def view_process_time_middleware(get_response):
def middleware(request):
print("before response", type(get_response), type(request))
response = get_response(request)
print("After response", type(response))
return response
return middleware
...
# settings.py (or config > settings > local.py)
MIDDLEWARE = [
...
'config.custom_middleware.view_process_time_middleware',
]
before response <class 'function'> <class 'django.core.handlers.wsgi.WSGIRequest'>
After response <class 'rest_framework.response.Response'>
response = get_response(request)
를 호출하여 get_response
를 사용하는 순간 다음 middleware를 가고 (리커시브 한 형태), response가 정의 된 순간이 view단 로직이 끝나고 우리 차례까지 넘오는 것이다. from time import process_time_ns
from django.core.handlers.wsgi import WSGIRequest
from rest_framework.response import Response
def view_process_time_middleware(get_response):
def middleware(request: WSGIRequest):
view_process_start_time = process_time_ns()
response: Response = get_response(request)
############ After response ############
view_process_end_time = process_time_ns()
if not response.has_header("process_time"):
response["View-Process-Run-Time"] = view_process_end_time - view_process_start_time
return response
return middleware
View-Process-Run-Time
값을 넣는데 성공했다!class ViewProcessTimeMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
view_process_start_time = process_time_ns()
response = self.get_response(request)
############ After response ############
view_process_end_time = process_time_ns()
if not response.has_header("process_time"):
response["View-Process-Run-Time"] = view_process_end_time - view_process_start_time
return response
클래스 형식으로 미들웨어를 정의하게 되면, http 요청/응답에 대한 처리를 하게되는 메소드를 추가로 정의할 수 있는데 이를 미들웨어 훅(Middleware Hook)이라고 한다.
공식 문서에 아래와 같이 정의 되어 있다.
- Calls self.process_request(request) (if defined).
- Calls self.get_response(request) to get the response from later - - middleware and the view.
- Calls self.process_response(request, response) (if defined).
process_request, process_view
, response는 process_exception, process_template_response, process_response
인 줄 알았는데,, 공식문서에서는 process_view, process_exception, process_template_response
가 official한 hook 종류였다. request
는, 위에서 확인 했 듯, HttpRequest object
이다.
view_func
는 django가 사용할 view function이다. (It’s the actual function object, not the name of the function as a string.)
view_args
는 view에서 넘어오는 positional arguments 것 이며, view_kwargs
는 view에서 넘어오는 dictionary of keyword arguments이다. view_args, view_kwargs 모두 첫 번째 인수인 request를 포함하지 않는다.
process_view()
는 Django가 view를 호출하기 "직전"에 trigger 된다.
이 hook 함수는 무조건 None아니면 HttpResponse object
를 return 해야 한다.
If it returns None, Django will continue processing this request, executing any other process_view() middleware and, then, the appropriate view.
If it returns an HttpResponse object, Django won’t bother calling the appropriate view; it’ll apply response middleware to that HttpResponse and return the result.
request
는 HttpRequest object
, exception
은 view에서 넘어오는 Exception object
. 만약 custom exception이라면 해당 부분 제대로 체크할 필요가 있다.
django는 view에서 exception있을 때 만 process_exception
를 호출한다.
이 hook 함수는 무조건 None아니면 HttpResponse object
를 return 해야 한다.
여기서 응답을 반환하면, 상위에 있는 process_exception 메서드는 호출되지 않는다. -> None일 때 만 타고 들어가니까.
request
는 HttpRequest object
, response
는 Django view or by a middleware에 의해 넘어오는 TemplateResponse object (or equivalent)
이다.
process_template_response
메서드는 view가 실행을 다 끝내자 마자 호출한다. 하지만 response instance에 render()
메서드나 그와 비슷한 역할을 하는 함수가 있어야만 호출이 된다.
이 hook 함수는 무조건 render method
에 의한 response object
를 return 해야 한다.
It could alter the given response by changing response.template_name and response.context_data, or it could create and return a brand-new TemplateResponse or equivalent.
You don’t need to explicitly render responses – responses will be automatically rendered once all template response middleware has been called.
Middleware are run in reverse order during the response phase, which includes process_template_response().