Middleware는 장고의 요청/응답 프로세스에 대한 Hook Framework로, 가벼운 low-level의 플러그인 시스템이다. 이를 통해 장고로 들어오는 모든 input과 output을 글로벌 하게 변경하는 것이 가능하며 이 때 각 middleware component는 특정 함수를 실행하는 역할을 한다.
장고에는 이미 내장된 middleware가 있는데 대표적으로 AuthenticationMiddleware
가 있다. 이는 세션을 사용해 사용자를 요청과 연결 한다.
middleware factory(아래의 예시에서는 simple_middleware)는 get_response
를 인자로 받아 middleware를 리턴하는 하는 callable이다. middleware 함수는 request를 인자로 받아 response를 리턴한다.
아래의 예시와 같이 사용할 수 있다.
함수형
def simple_middleware(get_response):
# One-time configuration and initialization.
def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
return middleware
클래스형
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
클래스형으로 쓸 경우 인스턴스를 호출할 수 있다.
실제로 django deprecation.py
에 있는 MiddlewareMixin
은 아래와 같이 생겼다.
- MiddlewareMixin
은 deprecation.py
에 있는데 deprecation.py
는 old-style을 new-style로 바꾸어 주는 역할을 한다. 장고의 middleware 스타일이 중간에 한 번 바뀌었기 때문에 이전 스타일의 middleware를 새로운 스타일로 변환시켜 주기 위해 사용하는 것이다(요약하자면, 하위 호환을 위한 것이다).
- 하위 호환은 MiddlewareMixin
의 __call__
함수를 호출함으로써 가능한데 이는 이전 스타일의 def middleware(request)
를 모방해서 새로운 스타일에 적용할 수 있도록 만들어 준다(스택 오버플로우에 자세한 설명이 나와 있다).
class MiddlewareMixin:
def __init__(self, get_response=None):
self.get_response = get_response
super().__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
response = response or self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
get_response
는 settings에 정의된 middleware 중 가장 마지막 원소이거나 middleware chain 중 현재 middleware의 다음 middleware에 해당하는 것이어야 한다.
- 즉, 현재 middleware 다음에 오는 어떤 middleware든 get_response
가 될 수 있다.
- 또한, get_response
는 실제 뷰에 해당한다.
실제로 middleware chain 중 가장 마지막에 오는 것은 실제 뷰는 아니며 handler의 wrapper 메소드로, view middleware를 관리하는 것에 해당한다.
- 즉, 적절한 URL 인자로 뷰를 호출하고 template-response와 exception middleware를 적용하는 역할을 한다.
Middleware를 활성화 하려면 settings.py의 MIDDLEWARE
환경 변수의 리스트에 middleware component를 추가하기만 하면 된다.
장고를 설치할 때 middleware는 비어 있어도 되지만 적어도 CommonMiddleware
즉 'django.middleware.common.CommonMiddleware'는 추가되어 있는 것이 좋다.
MIDDLEWARE
에서 순서는 중요한데 왜냐하면 하나의 middleware가 다른 middleware에 의존할 수도 있기 때문이다.
- 예를 들어, AuthenticationMiddleware
의 경우 인증된 유저를 세션에 저장하기 때문에 SessionMiddleware
다음에 실행 되어야 한다.
- 즉, 아래와 같이 MIDDLEWARE
가 정의되어 있다면 0번째 원소 -> 1번째 원소 -> ... 순으로 middleware가 실행 된다.
MIDDLEWARE = [
'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
에 정의된 순서대로(top-down) 미들웨어를 적용한다.get_response
를 호출해 다른 middleware를 호출하는 과정을 거치지 않고 바로 응답을 반환하기로 한다면 view를 포함한 내부에 있는 layer들은 요청 또는 응답을 확인할 수 없게 된다.