Hejmo/Blogo/Django-Middlewares

Django-Middlewares

#django #python #middleware
Django-Middlewares

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:

Kial Middlewares estas Gravaj en Django?

Middlewares estas fundamentaj en Django pro pluraj kialoj:

  1. Disigo de respondecoj: Ili permesas elpreni komunan funkciecon el la kodo de vidaĵoj, tenante ilin pli puraj kaj fokusigitaj.

  2. Rekodo-reuzo: Ili implementas funkciojn kiuj povas esti aplikitaj al pluraj vidaĵoj aŭ eĉ al ĉiuj petoj.

  3. Implementado de tutmondaj funkcioj: Ili estas idealaj por implementi:

    • Sekurecon (CSRF-protekto, SSL-redirektado)
    • Uzantaj seancoj
    • Respondo-densigo
    • Kaŝmemora regado
    • Aŭtentikado
    • Internaciigo
  4. 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:

Paŝoj por Ĝusta Agordado de Middlewares

  1. Identigi la bezonojn de via projekto: Ĉu vi bezonas plian sekurecon? Alirkontrolon? Monitoradon de aktiveco?

  2. Esplori disponeblajn middleware-ojn: Django inkluzivas multajn defaŭltajn middleware-ojn kiuj povas kontentigi komunajn bezonojn.

  3. Determini la ĝustan ordon: Kelkaj middleware-oj devas ruli antaŭ aliaj. Ekzemple, SessionMiddleware devas ruli antaŭ AuthenticationMiddleware ĉar aŭtentikado bezonas aliron al seancoj.

  4. 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.

  5. 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:

  1. Kodo en __init__ ruliĝas kiam Django startas
  2. Por ĉiu peto, kodo antaŭ self.get_response(request) ruliĝas
  3. La vidaĵo kaj iuj ajn postaj middleware-oj estas proceditaj
  4. Kodo post self.get_response(request) ruliĝas
  5. 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:

  1. Ĝi ruliĝas ĉe ĉiu HTTP-peto
  2. Ĝi kontrolas la aŭtentikadan staton de la uzanto (request.user.is_authenticated)
  3. Ĝi permesas aliron al publikaj vojoj (ensaluto, registriĝo, statikaj dosieroj)
  4. Ĝ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:

  1. Protokolas la eraron kun detaloj en la sistemaj protokoloj
  2. 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:

  1. Datumkolektado: Kaptas informojn kiel la alirita vojo, HTTP-metodo, klient-IP-adreso, kaj aŭtentikigita uzanto.
  2. Rendimento-mezurado: Registras kiom da tempo necesis por procezi la peton.
  3. Struktura protokolado: Konservas la datumojn en JSON-formato por faciligi postan analizon.

Ĉi tiu tipo de middleware estas speciale utila por:

Enkonstruitaj Middlewares en Django

Django inkluzivas plurajn antaŭdifinitajn middleware-ojn kiuj provizas esencajn funkciojn:

SecurityMiddleware

'django.middleware.security.SecurityMiddleware'

Implementas diversajn sekurecajn mezurojn:

Ĉ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:

CsrfViewMiddleware

'django.middleware.csrf.CsrfViewMiddleware'

Protektas kontraŭ Cross-Site Request Forgery (CSRF) atakoj:

Ĉ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:

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:

LocaleMiddleware

'django.middleware.locale.LocaleMiddleware'

Ebligas lingvoelekton bazitan sur petdatumoj, decidiga por multlingvaj retejoj:

  1. Ekzamenas la Accept-Language kapon de la retumilo
  2. Kontrolas la uzantan agordon se aŭtentikigita
  3. 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:

  1. SecurityMiddleware devas esti ĉe la komenco se vi aktivigos SSL-redirektojn por eviti nenecesajn procezadojn.

  2. UpdateCacheMiddleware devas veni antaŭ middleware-oj kiuj modifas la Vary kapon (SessionMiddleware, GZipMiddleware, LocaleMiddleware).

  3. GZipMiddleware devas veni antaŭ iu ajn middleware kiu povus ŝanĝi aŭ uzi la respondan korpon.

  4. SessionMiddleware devas veni antaŭ iu ajn middleware kiu uzas la seancajn konservdatumojn.

  5. AuthenticationMiddleware devas veni post SessionMiddleware ĉar ĝi uzas ĝiajn datumojn.

  6. MessageMiddleware devas veni post SessionMiddleware se vi uzas seancbazitan konservadon.

  7. FlatpageFallbackMiddleware kaj RedirectFallbackMiddleware 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:

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:

Konkludoj

Middleware-oj estas fundamenta parto de la arkitekturo de Django kiu ebligas:

  1. Centralizi komunan logikon por apliki ĝin al pluraj vidaĵoj sen ripeti kodon.
  2. Plibonigi sekurecon per implementado de aplikaĵnivelaj protektoj.
  3. Optimumigi rendimenton per kunpremado kaj kaŝmemorado.
  4. 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.

Reiri al artikoloj