Advanced Django Middleware 2026: Custom Middleware, Logging, dan Pertanyaan Interview

Panduan lengkap Django middleware di tahun 2026 mencakup pembuatan custom middleware, implementasi logging yang efektif, serta pertanyaan interview yang sering muncul untuk developer Django tingkat senior.

Diagram arsitektur Django middleware menunjukkan alur request-response

Django middleware berperan sebagai lapisan penting dalam setiap siklus request-response, namun banyak developer yang memperlakukannya sebagai kotak hitam. Pemahaman mendalam tentang eksekusi middleware, kapan harus menulis custom middleware, dan bagaimana mengimplementasikan logging pada level middleware menjadi pembeda antara developer Django senior dan junior dalam technical interview.

Django 6.0 Middleware

Django 6.0 mendukung penuh async middleware dengan sintaks async/await native. Atur async_capable = True dan sync_capable = False pada middleware class untuk menangani request tanpa overhead thread.

Eksekusi Django Middleware dalam Request-Response Cycle

Middleware di Django mengikuti model "onion" atau bawang. Ketika request tiba, ia melewati setiap middleware di MIDDLEWARE dari atas ke bawah. Ketika view mengembalikan response, response tersebut melewati middleware dari bawah ke atas. Urutan ini sangat penting: middleware yang membutuhkan data session harus dijalankan setelah SessionMiddleware.

Konfigurasi MIDDLEWARE default di Django 6.0:

python
# settings.py
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",
]

Setiap middleware dapat memotong rantai dengan mengembalikan HttpResponse secara langsung tanpa memanggil get_response(). Ketika ini terjadi, middleware bagian dalam dan view tidak akan pernah dieksekusi, tetapi middleware bagian luar tetap memproses response.

Pola middleware modern memerlukan class dengan method __init__ dan __call__. Method __init__ berjalan sekali ketika server dimulai, sedangkan __call__ berjalan untuk setiap request.

python
# middleware/timing.py
import time
import logging

logger = logging.getLogger(__name__)

class RequestTimingMiddleware:
    """Mencatat waktu yang dibutuhkan untuk memproses setiap request."""

    def __init__(self, get_response):
        # Dipanggil sekali ketika server dimulai
        self.get_response = get_response

    def __call__(self, request):
        # Kode sebelum view dieksekusi
        start_time = time.perf_counter()

        # Memanggil middleware berikutnya atau view
        response = self.get_response(request)

        # Kode setelah view dieksekusi
        duration_ms = (time.perf_counter() - start_time) * 1000
        logger.info(
            "Request %s %s selesai dalam %.2fms",
            request.method,
            request.path,
            duration_ms
        )

        return response

Daftarkan middleware di settings.py dengan menambahkan import path-nya ke MIDDLEWARE. Posisi sangat penting: tempatkan timing middleware di awal daftar untuk mengukur lifecycle request secara penuh.

Async Middleware untuk Aplikasi Django High-Concurrency

Django 6.0 menangani async request secara native. Untuk aplikasi yang berjalan di bawah ASGI dengan async views, sync middleware menciptakan bottleneck performa karena Django membungkus sync middleware dalam thread pool executors. Menulis async middleware menghilangkan overhead ini.

python
# middleware/async_timing.py
import time
import logging
from asgiref.sync import iscoroutinefunction, markcoroutinefunction

logger = logging.getLogger(__name__)

class AsyncRequestTimingMiddleware:
    """Async-native timing middleware untuk deployment ASGI."""

    async_capable = True
    sync_capable = False

    def __init__(self, get_response):
        self.get_response = get_response
        if iscoroutinefunction(self.get_response):
            markcoroutinefunction(self)

    async def __call__(self, request):
        start_time = time.perf_counter()

        # Await middleware berikutnya atau async view
        response = await self.get_response(request)

        duration_ms = (time.perf_counter() - start_time) * 1000
        logger.info(
            "Async request %s %s selesai dalam %.2fms",
            request.method,
            request.path,
            duration_ms
        )

        return response

Decorator @sync_and_async_middleware dari django.utils.decorators membuat middleware yang menangani request sync dan async dengan memeriksa iscoroutinefunction(get_response) saat runtime.

Implementasi Request ID Logging Middleware

Mengorelasikan log di sistem terdistribusi memerlukan identifier unik per request. Pola ini sering muncul dalam pertanyaan interview Django karena mendemonstrasikan pemahaman tentang middleware, logging, dan debugging sistem produksi.

python
# middleware/request_id.py
import uuid
import logging

class RequestIDMiddleware:
    """Melampirkan request ID unik ke setiap request untuk korelasi log."""

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Generate atau ekstrak request ID dari header
        request_id = request.headers.get("X-Request-ID")
        if not request_id:
            request_id = str(uuid.uuid4())

        # Lampirkan ke objek request untuk akses di views
        request.request_id = request_id

        response = self.get_response(request)

        # Tambahkan request ID ke response headers
        response["X-Request-ID"] = request_id

        return response

Untuk menyertakan request_id di semua pesan log, buat logging filter khusus:

python
# logging_filters.py
import logging

class RequestIDFilter(logging.Filter):
    """Menyuntikkan request_id ke dalam log records."""

    def filter(self, record):
        from django.middleware.request_id import local
        record.request_id = getattr(local, "request_id", "no-request-id")
        return True

Konfigurasikan filter di settings.py:

python
# settings.py
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "filters": {
        "request_id": {
            "()": "logging_filters.RequestIDFilter",
        },
    },
    "formatters": {
        "verbose": {
            "format": "[{levelname}] {asctime} [{request_id}] {name}: {message}",
            "style": "{",
        },
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "verbose",
            "filters": ["request_id"],
        },
    },
    "root": {
        "handlers": ["console"],
        "level": "INFO",
    },
}

Siap menguasai wawancara Django Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Hook Method Middleware untuk Kontrol Detail

Selain __call__, Django middleware mendukung tiga hook method: process_view, process_exception, dan process_template_response. Method ini memberikan kontrol pada titik-titik spesifik dalam lifecycle request.

process_view untuk Logika Pre-Execution

python
# middleware/permission_check.py
from django.http import HttpResponseForbidden

class PermissionCheckMiddleware:
    """Memeriksa permission sebelum eksekusi view."""

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

    def process_view(self, request, view_func, view_args, view_kwargs):
        # Akses metadata view
        required_permission = getattr(view_func, "required_permission", None)

        if required_permission:
            if not request.user.has_perm(required_permission):
                return HttpResponseForbidden("Permission denied")

        # Return None untuk melanjutkan ke view
        return None

Pola ini memungkinkan mendekorasi views dengan permission requirements:

python
# views.py
def require_permission(perm):
    def decorator(view_func):
        view_func.required_permission = perm
        return view_func
    return decorator

@require_permission("app.can_edit")
def edit_resource(request, pk):
    # Logika view
    pass

process_exception untuk Error Handling Terpusat

python
# middleware/error_tracking.py
import logging
import traceback

logger = logging.getLogger(__name__)

class ErrorTrackingMiddleware:
    """Mencatat exception dengan konteks lengkap sebelum Django menanganinya."""

    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):
        logger.error(
            "Unhandled exception di %s %s: %s\n%s",
            request.method,
            request.path,
            str(exception),
            traceback.format_exc(),
            extra={
                "user_id": getattr(request.user, "id", None),
                "request_id": getattr(request, "request_id", None),
            }
        )
        # Return None untuk membiarkan Django menangani exception
        return None

Method process_exception middleware dieksekusi dalam urutan terbalik. Jika salah satu mengembalikan HttpResponse, middleware yang lebih awal dalam stack tidak akan melihat exception tersebut.

Update Security Middleware Django 6.0

Django 6.0 dilengkapi dengan beberapa perbaikan keamanan untuk UpdateCacheMiddleware. Menurut Django security releases dari 2026, response dengan Cache-Control: private yang menggunakan mixed case salah di-cache, dan response terhadap request dengan header Authorization dapat disimpan di shared caches.

LoginRequiredMiddleware, yang diperkenalkan di Django 5.1, memaksa autentikasi site-wide:

python
# settings.py
MIDDLEWARE = [
    # ... middleware lainnya
    "django.contrib.auth.middleware.LoginRequiredMiddleware",
]

# Kecualikan view tertentu
from django.contrib.auth.decorators import login_not_required

@login_not_required
def public_view(request):
    return HttpResponse("Konten publik")

Ini membalikkan pola tradisional mendekorasi protected views dengan @login_required. Untuk aplikasi di mana sebagian besar views memerlukan autentikasi, ini mengurangi boilerplate dan mencegah eksposur endpoint protected secara tidak sengaja.

MiddlewareMixin dan Migrasi dari Legacy Middleware

MiddlewareMixin menjembatani middleware gaya lama (menggunakan method process_request dan process_response) ke pola callable baru. Perhatikan bahwa django.utils.deprecation.MiddlewareMixin sudah deprecated dan akan dihapus di Django 6.2. Gunakan django.middleware.MiddlewareMixin sebagai gantinya.

python
# middleware/legacy_style.py
from django.middleware import MiddlewareMixin

class LegacyStyleMiddleware(MiddlewareMixin):
    """Middleware menggunakan pola hook-based legacy."""

    def process_request(self, request):
        # Dipanggil sebelum view, return None untuk melanjutkan
        request.custom_attribute = "value"
        return None

    def process_response(self, request, response):
        # Dipanggil setelah view, harus return response
        response["X-Custom-Header"] = "middleware-added"
        return response

Untuk middleware baru, hindari MiddlewareMixin. Pola callable memberikan kontrol flow yang lebih jelas dan kompatibilitas lebih baik dengan async Django.

Pertanyaan Interview Django Middleware yang Umum

Technical interview sering menyertakan pertanyaan middleware untuk menilai pemahaman tentang lifecycle request Django. Pertanyaan-pertanyaan ini sering muncul dalam interview developer Django senior.

T: Apa perbedaan antara urutan middleware dan urutan eksekusi untuk response?

Middleware memproses request dari atas ke bawah melalui MIDDLEWARE tetapi memproses response dari bawah ke atas. Bayangkan seperti bawang: middleware terluar adalah yang pertama melihat request dan terakhir melihat response.

T: Bagaimana cara kerja middleware short-circuiting?

Ketika middleware mengembalikan HttpResponse alih-alih memanggil get_response(), request tidak pernah mencapai middleware dalam atau view. Response tetap melewati semua middleware luar dalam perjalanan kembali. Perilaku ini berbeda dari setting MIDDLEWARE_CLASSES legacy, di mana semua method process_response berjalan terlepas dari short-circuiting.

T: Kapan middleware harus mengakses request.user?

Hanya setelah AuthenticationMiddleware berjalan. Mengakses request.user sebelum authentication middleware melampirkannya akan menimbulkan AttributeError. Tempatkan custom middleware yang membutuhkan informasi user setelah AuthenticationMiddleware dalam daftar MIDDLEWARE.

T: Bagaimana cara membuat middleware async-compatible?

Atur async_capable = True pada middleware class dan implementasikan __call__ sebagai async method menggunakan await get_response(request). Untuk middleware yang harus bekerja di konteks sync dan async, gunakan @sync_and_async_middleware dan periksa iscoroutinefunction(get_response) untuk bercabang sesuai kebutuhan.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Menguasai Django Middleware untuk Produksi dan Interview

Django middleware mengontrol cross-cutting concerns yang mencakup seluruh lifecycle request. Poin-poin penting untuk bekerja dengan middleware di Django 6.0:

  • Middleware dieksekusi dalam pola onion: request mengalir dari atas ke bawah, response mengalir dari bawah ke atas
  • Atur async_capable = True untuk async-native middleware di deployment ASGI untuk menghindari overhead thread pool
  • Gunakan process_view untuk memeriksa metadata view sebelum eksekusi, process_exception untuk logging error terpusat
  • Request ID middleware memungkinkan korelasi log di layanan terdistribusi, kebutuhan umum di produksi
  • LoginRequiredMiddleware membalikkan pola autentikasi: views dilindungi secara default, views publik dikecualikan secara eksplisit
  • django.utils.deprecation.MiddlewareMixin yang deprecated pindah ke django.middleware.MiddlewareMixin di Django 6.2
  • Urutan middleware menentukan komponen mana yang melihat request.user, data session, dan atribut request lainnya

Untuk pembahasan lebih mendalam tentang pola Django ORM dan REST framework, lihat panduan optimasi Django ORM.

Tantangan harian

Bisakah kamu menemukan bug di Django?

Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Anthony Fillion-Maillet

Ditulis oleh

Anthony Fillion-Maillet

Pendiri SharpSkill

Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.

Diperbarui 18 September 2026

Tag

#django
#middleware
#python
#logging
#interview

Bagikan

Artikel terkait