Enkonduko al Middlewares en Django
Kio estas Middleware?
Middleware en Django estas Python-klaso kiu funkcias kiel “peranto” inter la HTTP-peto kaj la respondo generita de la vidaĵo. Middlewares estas komponentoj kiuj procezas petojn antaŭ ol ili atingas la vidaĵon kaj/aŭ respondojn antaŭ ol ili estas senditaj al la retumilo.
Ĉiu middleware havas la kapablon plenumi agojn je malsamaj momentoj en la vivociklo de peto:
- Antaŭ ol Django procezas peton
- Dum la procezado de vidaĵo
- Post kiam vidaĵo generis respondon, sed antaŭ ol Django sendas ĝin
Kial Middlewares estas Gravaj en Django?
Middlewares estas fundamentaj en Django pro pluraj kialoj:
-
Disigo de respondecoj: Ili permesas elpreni komunan funkciecon el la kodo de vidaĵoj, tenante ilin pli puraj kaj fokusigitaj.
-
Rekodo-reuzo: Ili implementas funkciojn kiuj povas esti aplikitaj al pluraj vidaĵoj aŭ eĉ al ĉiuj petoj.
-
Implementado de tutmondaj funkcioj: Ili estas idealaj por implementi:
- Sekurecon (CSRF-protekto, SSL-redirektado)
- Uzantaj seancoj
- Respondo-densigo
- Kaŝmemora regado
- Aŭtentikado
- Internaciigo
-
Modifado de pet-fluo: Ili povas interkepti petojn kaj redirektigi, malakcepti aŭ modifi ilin laŭbezone.
Agordado de Middlewares en Django
Kiel Agordi Middleware
Por uzi middleware-ojn en Django, vi devas agordi ilin en la MIDDLEWARE
listo ene de la settings.py
dosiero de via projekto:
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',
# Viaj propraj middleware-oj ĉi tie
]
La ordo de middleware-oj en ĉi tiu listo estas decidiga, ĉar ĝi determinas la ordon en kiu petoj kaj respondoj estas proceditaj:
- HTTP-petoj trairas la middleware-ojn de supre malsupren.
- HTTP-respondoj trairas ilin de malsupre supren.
Paŝoj por Ĝusta Agordado de Middlewares
-
Identigi la bezonojn de via projekto: Ĉu vi bezonas plian sekurecon? Alirkontrolon? Monitoradon de aktiveco?
-
Esplori disponeblajn middleware-ojn: Django inkluzivas multajn defaŭltajn middleware-ojn kiuj povas kontentigi komunajn bezonojn.
-
Determini la ĝustan ordon: Kelkaj middleware-oj devas ruli antaŭ aliaj. Ekzemple,
SessionMiddleware
devas ruli antaŭAuthenticationMiddleware
ĉar aŭtentikado bezonas aliron al seancoj. -
Testi la agordon: Post agordi viajn middleware-ojn, estas grave testi ke ĉio funkcias korekte, precipe se vi ŝanĝis la ordon de la defaŭltaj middleware-oj.
-
Konsideri la rendimenton: Ĉiu middleware aldonas procez-tempon al ĉiu peto. Uzu nur tiujn kiujn vi vere bezonas.
Kreado de Viaj Propraj Adaptitaj Middlewares
Baza Strukturo de Middleware
Ekde Django 1.10, middleware-oj estas implementitaj kiel klasoj kun specifa strukturo:
class MiaMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# Unufoja inicializa kodo
def __call__(self, request):
# Kodo kiu ruliĝas antaŭ la vidaĵo (kaj aliaj middleware-oj)
response = self.get_response(request)
# Kodo kiu ruliĝas post la vidaĵo
return response
La proceza fluo estas:
- Kodo en
__init__
ruliĝas kiam Django startas - Por ĉiu peto, kodo antaŭ
self.get_response(request)
ruliĝas - La vidaĵo kaj iuj ajn postaj middleware-oj estas proceditaj
- Kodo post
self.get_response(request)
ruliĝas - La respondo estas redonita
Specialaj Metodoj por Specifaj Kazoj
Krom la metodo __call__
, vi povas implementi specialajn metodojn por interveni ĉe specifaj tempoj:
process_view(request, view_func, view_args, view_kwargs)
Ĉi tiu metodo ruliĝas ĵus antaŭ ol Django vokas la vidaĵon, sed post kiam la URL estas procezita kaj la responda vidaĵ-funkcio estas solvita.
def process_view(self, request, view_func, view_args, view_kwargs):
# Logiko antaŭ la vidaĵo
return None # Se ĝi redonas None, Django daŭrigos procezadon
# Ĝi ankaŭ povas redoni HttpResponse por haltigi procezadon
process_exception(request, exception)
Ruliĝas kiam vidaĵo levas netraktitan escepton.
def process_exception(self, request, exception):
# Logiko por trakti esceptojn
return None # Se ĝi redonas None, la escepto disvastiĝas
# Aŭ ĝi povas redoni adaptitan HTTP-respondon
process_template_response(request, response)
Ruliĝas se la respondo havas metodon render()
, kutime kiam TemplateResponse
estas uzata.
def process_template_response(self, request, response):
# Povas modifi response.context_data aŭ response.template_name
return response # Devas redoni respondon kun render() metodo
Kompleta Ekzemplo: Rendimento-Monitora Middleware
import time
import statistics
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
class PerformanceMonitorMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.timings = []
self.threshold = getattr(settings, 'SLOW_REQUEST_THRESHOLD', 500) # ms
def __call__(self, request):
start_time = time.time()
response = self.get_response(request)
duration = (time.time() - start_time) * 1000 # Konverti al milisekundoj
self.timings.append(duration)
# Protokoli malrapidajn petojn
if duration > self.threshold:
self._log_slow_request(request, duration)
# Aldoni kapon kun respondo-tempo
response['X-Response-Time-ms'] = str(int(duration))
# Ĉiujn 100 petojn, kalkuli kaj protokoli statistikojn
if len(self.timings) >= 100:
self._calculate_stats()
self.timings = [] # Restarigi la liston
return response
def _log_slow_request(self, request, duration):
"""Protokoli malrapidajn petojn por posta esploro."""
import logging
logger = logging.getLogger('performance')
logger.warning(
f"Malrapida peto: {request.method} {request.path} - {int(duration)}ms"
)
def _calculate_stats(self):
"""Kalkuli rendimento-statistikojn."""
import logging
logger = logging.getLogger('performance')
if not self.timings:
return
stats = {
'count': len(self.timings),
'avg': statistics.mean(self.timings),
'median': statistics.median(self.timings),
'min': min(self.timings),
'max': max(self.timings),
'p95': sorted(self.timings)[int(len(self.timings) * 0.95)]
}
logger.info(
f"Rendimento-statistikoj: "
f"meznombro={stats['avg']:.2f}ms, "
f"mediano={stats['median']:.2f}ms, "
f"p95={stats['p95']:.2f}ms, "
f"maksimume={stats['max']:.2f}ms"
)
Ekzemploj de Middlewares en Django
Ekzemplo 1: Aŭtentikada Middleware
Ekzempla kodo
# En auth_middleware.py
from django.http import HttpResponseRedirect
from django.conf import settings
from django.urls import reverse
class AuthRequiredMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Vojoj kiuj ne postulas aŭtentikadon
public_paths = [reverse('login'), reverse('register'), '/static/']
# Kontroli ĉu la uzanto estas aŭtentikigita aŭ la vojo estas publika
if not request.user.is_authenticated and not any(request.path.startswith(path) for path in public_paths):
return HttpResponseRedirect(settings.LOGIN_URL)
return self.get_response(request)
Klarigo pri kiel ĝi funkcias
Ĉi tiu middleware kontrolas ĉu la uzanto estas aŭtentikigita. Se la uzanto ne estas ensalutinta kaj provas aliri protektitan vojon, li estos redirektita al la ensaluta paĝo.
Ĉefaj trajtoj:
- Ĝi ruliĝas ĉe ĉiu HTTP-peto
- Ĝi kontrolas la aŭtentikadan staton de la uzanto (
request.user.is_authenticated
) - Ĝi permesas aliron al publikaj vojoj (ensaluto, registriĝo, statikaj dosieroj)
- Ĝi redirektadas al la ensaluta paĝo se necese
Por uzi ĉi tiun middleware, aldonu ĝin al la MIDDLEWARE
listo en via dosiero settings.py, certigante ke vi metas ĝin post la aŭtentikada middleware:
MIDDLEWARE = [
# ...aliaj middleware-oj
'django.contrib.auth.middleware.AuthenticationMiddleware', # Devas antaŭiri
'vojo.al.via.app.auth_middleware.AuthRequiredMiddleware',
# ...aliaj middleware-oj
]
Ekzemplo 2: Erartraktada Middleware
Ekzempla kodo
# En error_handling_middleware.py
import logging
import traceback
from django.http import HttpResponse, JsonResponse
logger = logging.getLogger('django')
class ErrorHandlingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_exception(self, request, exception):
# Protokoli la escepton
logger.error(f"Netraktita escepto: {exception}")
logger.error(traceback.format_exc())
# Por AJAX-petoj, redoni eraron en JSON-formato
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
return JsonResponse({
'error': True,
'message': str(exception)
}, status=500)
# Por normalaj petoj, vi povas montri adaptitan erarŝablonon
return HttpResponse(f"Eraro okazis: {exception}", status=500)
Klarigo pri kiel ĝi funkcias
Ĉi tiu middleware kaptas ajnajn netraktitajn esceptojn en la vidaĵoj kaj:
- Protokolas la eraron kun detaloj en la sistemaj protokoloj
- Provizas taŭgan respondon laŭ la tipo de peto:
- Por AJAX-petoj, redonas eraron en JSON-formato
- Por ordinaraj petoj, montras amikan erarmesaĝon
Male al la antaŭa ekzemplo, ĉi tiu middleware implementas la metodon process_exception
, kiun Django vokas kiam vidaĵo levas netraktitan escepton. Tio ebligas centraligi erartraktadon kaj provizi konsekvencajn respondojn.
Ekzemplo 3: Aktiveca Protokolada Middleware
Ekzempla kodo
# En activity_logger_middleware.py
import time
import json
import logging
from datetime import datetime
logger = logging.getLogger('activity')
class ActivityLoggerMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Protokoli komenctempo
start_time = time.time()
# Bazaj informoj pri la peto
log_data = {
'timestamp': datetime.now().isoformat(),
'path': request.path,
'method': request.method,
'ip': self._get_client_ip(request),
'user': str(request.user) if hasattr(request, 'user') else 'AnonymousUser',
}
# Procezi la peton
response = self.get_response(request)
# Protokoli fintempo kaj daŭro
duration = time.time() - start_time
log_data['duration'] = round(duration * 1000, 2) # Milisekundoj
log_data['status_code'] = response.status_code
# Protokoli la informojn
logger.info(json.dumps(log_data))
return response
def _get_client_ip(self, request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
Klarigo pri kiel ĝi funkcias
Ĉi tiu middleware protokolas detalajn informojn pri ĉiu peto kiun via Django-aplikaĵo ricevas:
- Datumkolektado: Kaptas informojn kiel la alirita vojo, HTTP-metodo, klient-IP-adreso, kaj aŭtentikigita uzanto.
- Rendimento-mezurado: Registras kiom da tempo necesis por procezi la peton.
- Struktura protokolado: Konservas la datumojn en JSON-formato por faciligi postan analizon.
Ĉi tiu tipo de middleware estas speciale utila por:
- Realtempan aktiveca monitoro
- Analizo de utiligmodelarojn
- Detektado de rendimento-problemojn
- Sekureca aŭditado
Enkonstruitaj Middlewares en Django
Django inkluzivas plurajn antaŭdifinitajn middleware-ojn kiuj provizas esencajn funkciojn:
SecurityMiddleware
'django.middleware.security.SecurityMiddleware'
Implementas diversajn sekurecajn mezurojn:
- HTTP Strict Transport Security (HSTS): Devigas HTTPS-konektojn
- Content-Type nosniff: Malhelpas retumilojn diveni la enhavotipon
- Referrer Policy: Kontrolas kiujn informojn estas sendataj en la Referer-kapo
- Cross-Origin Opener Policy: Izolas supernivelajn fenestrojn disde aliaj dokumentoj
- SSL-Redirektado: Aŭtomate redirektadas de HTTP al HTTPS
Ĉefa agordaro:
# settings.py
SECURE_HSTS_SECONDS = 3600 # Daŭro en sekundoj por HSTS
SECURE_HSTS_INCLUDE_SUBDOMAINS = True # Apliki HSTS al subdomajnoj
SECURE_SSL_REDIRECT = True # Redirektigi HTTP al HTTPS
SECURE_CONTENT_TYPE_NOSNIFF = True # Aktivigi X-Content-Type-Options
SessionMiddleware
'django.contrib.sessions.middleware.SessionMiddleware'
Administras uzantajn seancojn, ebligante konservi kaj retrovi datumojn inter petoj. Ĝi estas fundamenta por funkcioj kiel aŭtentikado, aĉet-korboj, aŭ uzantaj preferoj.
Vi bezonas agordi la konservadan backend en settings.py
:
# Defaŭlte, konservas seancojn en la datumbazo
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
# Aliaj opcioj inkluzivas subskribitajn kuketojn
# SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
# Aŭ uzante kaŝmemoron por plibona rendimento
# SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
CommonMiddleware
'django.middleware.common.CommonMiddleware'
Provizas plurajn praktikajn oportunaĵojn:
- Append Slash: Aldonas finan oblikvan strion al URL-oj se
APPEND_SLASH=True
- Prepend WWW: Aldonas “www.” al la komenco de URL-oj se
PREPEND_WWW=True
- Blokado de user-agents: Blokas retumilojn aŭ robotojn laŭ
DISALLOWED_USER_AGENTS
- Content-Length: Establas la Content-Length kapon por ne-fluantaj respondoj
CsrfViewMiddleware
'django.middleware.csrf.CsrfViewMiddleware'
Protektas kontraŭ Cross-Site Request Forgery (CSRF) atakoj:
- Aldonas CSRF-ĵetonon al HTML-formularoj
- Kontrolas ĉi tiun ĵetonon en POST-petoj
- Malakceptas petojn kiuj ne inkluzivas validan ĵetonon
Ĉi tiu middleware estas decida por sekureco, precipe en formularoj kiuj faras gravajn ŝanĝojn (pagoj, pasvortoŝanĝoj, ktp.).
AuthenticationMiddleware
'django.contrib.auth.middleware.AuthenticationMiddleware'
Aldonas la user
atributon al ĉiu request
objekto, ebligante aliri la nunan uzanton de iu ajn vidaĵo aŭ posta middleware.
Ĉi tiu middleware ebligas uzi request.user
kaj kontroli ĉu uzanto estas aŭtentikigita per request.user.is_authenticated
.
MessageMiddleware
'django.middleware.messages.MessageMiddleware'
Implementas mesaĝsistemon por komuniki informojn al la uzanto inter petoj:
- Sukcesaj mesaĝoj post formulara sendo
- Erarmesaĝoj kiam io malsukcesas
- Ĝenerala informado aŭ avertoj
Uzekzemplo en vidaĵoj:
from django.contrib import messages
def mia_vidajo(request):
# ... vidaĵa logiko ...
messages.success(request, 'Operacio sukcese kompletita!')
# ... pli da logiko ...
GZipMiddleware
'django.middleware.gzip.GZipMiddleware'
Aŭtomate kunpremas respondojn por retumiloj kiuj subtenas GZip-kunpremadon, reduktante la bezonatan bendolarĝon kaj plibonigante ŝarĝtempon.
Ĝi ne kunpremos:
- Enhavon malpli ol 200 bajtoj
- Respondojn kiuj jam havas Content-Encoding kapon
- Petojn de retumiloj kiuj ne subtenas kunpremadon
LocaleMiddleware
'django.middleware.locale.LocaleMiddleware'
Ebligas lingvoelekton bazitan sur petdatumoj, decidiga por multlingvaj retejoj:
- Ekzamenas la Accept-Language kapon de la retumilo
- Kontrolas la uzantan agordon se aŭtentikigita
- Subtenas URL-ojn kun lingva prefikso (/eo/, /en/, ktp.)
Por uzi ĉi tiun middleware, certigu difini LANGUAGES
en via settings.py:
LANGUAGES = [
('eo', 'Esperanto'),
('en', 'Angla'),
('fr', 'Franca'),
]
Ordigado de Middlewares
La ordo de middleware en la MIDDLEWARE
listo de Django estas kritika. Sekvante la oficialan dokumentaron de Django, jen kelkaj rekomendoj por ĝusta ordigado:
-
SecurityMiddleware
devas esti ĉe la komenco se vi aktivigos SSL-redirektojn por eviti nenecesajn procezadojn. -
UpdateCacheMiddleware
devas veni antaŭ middleware-oj kiuj modifas laVary
kapon (SessionMiddleware, GZipMiddleware, LocaleMiddleware). -
GZipMiddleware
devas veni antaŭ iu ajn middleware kiu povus ŝanĝi aŭ uzi la respondan korpon. -
SessionMiddleware
devas veni antaŭ iu ajn middleware kiu uzas la seancajn konservdatumojn. -
AuthenticationMiddleware
devas veni postSessionMiddleware
ĉar ĝi uzas ĝiajn datumojn. -
MessageMiddleware
devas veni postSessionMiddleware
se vi uzas seancbazitan konservadon. -
FlatpageFallbackMiddleware
kajRedirectFallbackMiddleware
devas esti preskaŭ ĉe la fino, ĉar ili estas lasta rimeda rimedoj.
Plej Bonaj Praktikoj por Labori kun Middlewares en Django
1. Principo de Unuopa Respondeco
Ĉiu middleware devus havi unuopan, klare difinitan respondecon. Se middleware faras tro da aferoj, konsideru dividi ĝin en plurajn komponentojn.
2. Konsideri Rendimenton
Memoru ke middleware-oj ruliĝas je ĉiu HTTP-peto, do estas grave teni ilin efikaj. Kelkaj rekomendoj:
- Evitu multekostajn operaciojn kiel datumbazajn demandojn aŭ eksterajn API-vokojn en la ĉefa fluo.
- Uzu kaŝmemorajn mekanismojn kiam eble.
- Konsideru uzi malsamajn agordojn laŭ medio (programada kontraŭ produktiva).
3. Testu Viajn Middlewares
Middleware-oj devus esti zorge testataj, ĉar eraro en ili povas influi la tutan aplikaĵon:
from django.test import TestCase, RequestFactory
from myapp.middleware import MyCustomMiddleware
class MyMiddlewareTest(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.middleware = MyCustomMiddleware(lambda request: "simulita respondo")
def test_middleware_functionality(self):
request = self.factory.get('/test-url/')
response = self.middleware(request)
# Kontroli ke la middleware funkcias ĝuste
self.assertEqual(response.status_code, 200)
4. Aktivigi Middleware-ojn Kondiĉe
Kelkfoje, vi volos aktivigi certajn middleware-ojn nur sub specifaj kondiĉoj:
# settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
# Aliaj bazaj middleware-oj
]
if DEBUG:
MIDDLEWARE += ['myproject.middleware.debug.DebugToolbarMiddleware']
if not DEBUG:
MIDDLEWARE += ['myproject.middleware.security.ExtraSecurityMiddleware']
5. Klara Dokumentado
Dokumentu klare la funkcion kaj la konduton de ĉiu adaptita middleware:
- Kion la middleware faras
- Kiam ĝi devus esti uzata
- Kie ĝi devus esti metita en la middleware-ĉeno
- Disponeblaj agordoj
- Eblaj flankaj efikoj
Konkludoj
Middleware-oj estas fundamenta parto de la arkitekturo de Django kiu ebligas:
- Centralizi komunan logikon por apliki ĝin al pluraj vidaĵoj sen ripeti kodon.
- Plibonigi sekurecon per implementado de aplikaĵnivelaj protektoj.
- Optimumigi rendimenton per kunpremado kaj kaŝmemorado.
- Personecigi aplikaĵan konduton laŭ viaj specifaj bezonoj.
Kvankam Django inkluzivas kompletan aron de middleware-oj por la plej komunaj bezonoj, lerni disvolvi viajn proprajn middleware-ojn donos al vi pli grandan kontrolon super la peto-responda ciklo kaj ebligos al vi implementi personecigitajn funkciojn kiuj plibonigas viajn aplikaĵojn.
Ĉiam memoru konsideri rendimentajn efikojn kaj zorge testi iun ajn personecigitan middleware antaŭ ol uzi ĝin en produktado.