Django Middleware ขั้นสูง 2026: Custom Middleware, Logging และคำถามสัมภาษณ์งาน
คู่มือฉบับสมบูรณ์เกี่ยวกับ Django middleware ในปี 2026 ครอบคลุมการสร้าง custom middleware การใช้งาน logging อย่างมีประสิทธิภาพ และคำถามสัมภาษณ์ที่พบบ่อยสำหรับนักพัฒนา Django ระดับ senior

Django middleware อยู่ที่แกนกลางของทุก request-response cycle แต่นักพัฒนาหลายคนยังคงมองว่ามันเป็นกล่องดำ การเข้าใจว่า middleware ทำงานอย่างไร เมื่อไหร่ควรเขียน custom middleware และวิธีการ implement logging ในระดับ middleware เป็นสิ่งที่แยกแยะนักพัฒนา Django ระดับ senior ออกจาก junior ในการสัมภาษณ์งานด้านเทคนิค
Django 6.0 รองรับ async middleware อย่างสมบูรณ์ด้วย syntax async/await แบบ native ตั้งค่า async_capable = True และ sync_capable = False บน middleware class เพื่อจัดการ request โดยไม่มี overhead จาก thread
วิธีการทำงานของ Django Middleware ใน Request-Response Cycle
Middleware ใน Django ใช้โมเดล "onion" (หัวหอม) เมื่อ request มาถึง มันจะผ่านแต่ละ middleware ใน MIDDLEWARE จากบนลงล่าง เมื่อ view ส่งคืน response นั้น response จะผ่านกลับผ่าน middleware จากล่างขึ้นบน ลำดับนี้มีความสำคัญ: middleware ที่ต้องการข้อมูล session ต้องทำงานหลัง SessionMiddleware
การกำหนดค่า MIDDLEWARE เริ่มต้นใน Django 6.0:
# 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",
]แต่ละ middleware สามารถตัดสายโซ่ได้โดยการส่งคืน HttpResponse โดยตรงแทนที่จะเรียก get_response() เมื่อสิ่งนี้เกิดขึ้น middleware ด้านในและ view จะไม่ถูกเรียกใช้ แต่ middleware ด้านนอกยังคงประมวลผล response
การเขียน Custom Middleware แบบ Class-Based ใน Django 6.0
รูปแบบ middleware สมัยใหม่ต้องการ class ที่มี method __init__ และ __call__ method __init__ ทำงานครั้งเดียวเมื่อ server เริ่มต้น ในขณะที่ __call__ ทำงานสำหรับทุก request
# middleware/timing.py
import time
import logging
logger = logging.getLogger(__name__)
class RequestTimingMiddleware:
"""บันทึกเวลาที่ใช้ในการประมวลผลแต่ละ request"""
def __init__(self, get_response):
# เรียกครั้งเดียวเมื่อ server เริ่มต้น
self.get_response = get_response
def __call__(self, request):
# โค้ดก่อนที่ view จะทำงาน
start_time = time.perf_counter()
# เรียก middleware ถัดไปหรือ view
response = self.get_response(request)
# โค้ดหลังจาก view ทำงานแล้ว
duration_ms = (time.perf_counter() - start_time) * 1000
logger.info(
"Request %s %s เสร็จสิ้นใน %.2fms",
request.method,
request.path,
duration_ms
)
return responseลงทะเบียน middleware ใน settings.py โดยเพิ่ม import path ลงใน MIDDLEWARE ตำแหน่งมีความสำคัญ: วาง timing middleware ไว้ต้นรายการเพื่อวัด lifecycle ของ request ทั้งหมด
Async Middleware สำหรับแอปพลิเคชัน Django ที่มี High-Concurrency
Django 6.0 จัดการ async request แบบ native สำหรับแอปพลิเคชันที่ทำงานภายใต้ ASGI พร้อมกับ async views sync middleware จะสร้างคอขวดด้านประสิทธิภาพเนื่องจาก Django จะ wrap sync middleware ใน thread pool executors การเขียน async middleware จะกำจัด overhead นี้
# middleware/async_timing.py
import time
import logging
from asgiref.sync import iscoroutinefunction, markcoroutinefunction
logger = logging.getLogger(__name__)
class AsyncRequestTimingMiddleware:
"""Async-native timing middleware สำหรับการ deploy 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 ถัดไปหรือ async view
response = await self.get_response(request)
duration_ms = (time.perf_counter() - start_time) * 1000
logger.info(
"Async request %s %s เสร็จสิ้นใน %.2fms",
request.method,
request.path,
duration_ms
)
return responseDecorator @sync_and_async_middleware จาก django.utils.decorators สร้าง middleware ที่จัดการทั้ง request แบบ sync และ async โดยตรวจสอบ iscoroutinefunction(get_response) ใน runtime
การ Implement Request ID Logging Middleware
การเชื่อมโยง log ในระบบแบบกระจายต้องการ identifier ที่ไม่ซ้ำกันต่อ request รูปแบบนี้ปรากฏบ่อยในคำถามสัมภาษณ์ Django เพราะมันแสดงให้เห็นถึงความเข้าใจเกี่ยวกับ middleware logging และการ debug ระบบ production
# middleware/request_id.py
import uuid
import logging
class RequestIDMiddleware:
"""แนบ request ID ที่ไม่ซ้ำกันกับทุก request เพื่อการเชื่อมโยง log"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# สร้างหรือดึง request ID จาก header
request_id = request.headers.get("X-Request-ID")
if not request_id:
request_id = str(uuid.uuid4())
# แนบกับ request object เพื่อเข้าถึงใน views
request.request_id = request_id
response = self.get_response(request)
# เพิ่ม request ID ลงใน response headers
response["X-Request-ID"] = request_id
return responseเพื่อรวม request_id ในทุกข้อความ log ให้สร้าง logging filter แบบกำหนดเอง:
# logging_filters.py
import logging
class RequestIDFilter(logging.Filter):
"""ฉีด request_id เข้าไปใน 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กำหนดค่า filter ใน settings.py:
# 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",
},
}พร้อมที่จะพิชิตการสัมภาษณ์ Django แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
Hook Methods ของ Middleware สำหรับการควบคุมอย่างละเอียด
นอกเหนือจาก __call__ แล้ว Django middleware ยังรองรับสาม hook methods: process_view, process_exception และ process_template_response ซึ่งให้การควบคุมที่จุดเฉพาะใน lifecycle ของ request
process_view สำหรับ Logic ก่อนการทำงาน
# middleware/permission_check.py
from django.http import HttpResponseForbidden
class PermissionCheckMiddleware:
"""ตรวจสอบ permission ก่อนที่ 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):
# เข้าถึง 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 เพื่อดำเนินการต่อไปยัง view
return Noneรูปแบบนี้ช่วยให้สามารถ decorate views ด้วยข้อกำหนด permission:
# 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):
# View logic
passprocess_exception สำหรับ Error Handling แบบรวมศูนย์
# middleware/error_tracking.py
import logging
import traceback
logger = logging.getLogger(__name__)
class ErrorTrackingMiddleware:
"""บันทึก exception พร้อมบริบทเต็มก่อนที่ Django จะจัดการ"""
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 ใน %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 เพื่อให้ Django จัดการ exception
return NoneMethods process_exception ของ middleware ทำงานในลำดับกลับกัน หากตัวหนึ่งส่งคืน HttpResponse middleware ที่อยู่ก่อนหน้าใน stack จะไม่เห็น exception
การอัพเดต Security Middleware ใน Django 6.0
Django 6.0 มาพร้อมกับการแก้ไขด้านความปลอดภัยหลายรายการสำหรับ UpdateCacheMiddleware ตามDjango security releases จาก 2026 response ที่มี Cache-Control: private ที่ใช้ mixed case ถูก cache อย่างไม่ถูกต้อง และ response สำหรับ request ที่มี header Authorization อาจถูกเก็บใน shared caches
LoginRequiredMiddleware ซึ่งเปิดตัวใน Django 5.1 บังคับใช้การยืนยันตัวตนทั่วทั้ง site:
# settings.py
MIDDLEWARE = [
# ... middleware อื่นๆ
"django.contrib.auth.middleware.LoginRequiredMiddleware",
]
# ยกเว้น view เฉพาะ
from django.contrib.auth.decorators import login_not_required
@login_not_required
def public_view(request):
return HttpResponse("เนื้อหาสาธารณะ")สิ่งนี้กลับด้านรูปแบบดั้งเดิมของการ decorate protected views ด้วย @login_required สำหรับแอปพลิเคชันที่ views ส่วนใหญ่ต้องการการยืนยันตัวตน สิ่งนี้ลด boilerplate และป้องกันการเปิดเผย endpoint ที่ได้รับการป้องกันโดยไม่ตั้งใจ
MiddlewareMixin และการ Migrate จาก Legacy Middleware
MiddlewareMixin เชื่อมต่อ middleware รูปแบบเก่า (ใช้ method process_request และ process_response) กับรูปแบบ callable ใหม่ โปรดทราบว่า django.utils.deprecation.MiddlewareMixin ถูก deprecated และจะถูกลบออกใน Django 6.2 ให้ใช้ django.middleware.MiddlewareMixin แทน
# middleware/legacy_style.py
from django.middleware import MiddlewareMixin
class LegacyStyleMiddleware(MiddlewareMixin):
"""Middleware ที่ใช้รูปแบบ hook-based แบบ legacy"""
def process_request(self, request):
# เรียกก่อน view, return None เพื่อดำเนินการต่อ
request.custom_attribute = "value"
return None
def process_response(self, request, response):
# เรียกหลัง view, ต้อง return response
response["X-Custom-Header"] = "middleware-added"
return responseสำหรับ middleware ใหม่ ให้หลีกเลี่ยง MiddlewareMixin รูปแบบ callable ให้การควบคุมการไหลที่ชัดเจนกว่าและเข้ากันได้ดีกว่ากับ async Django
คำถามสัมภาษณ์เกี่ยวกับ Django Middleware ที่พบบ่อย
การสัมภาษณ์ทางเทคนิคมักรวมคำถามเกี่ยวกับ middleware เพื่อประเมินความเข้าใจเกี่ยวกับ lifecycle ของ request ใน Django คำถามเหล่านี้มักปรากฏในการสัมภาษณ์นักพัฒนา Django ระดับ senior
ถ: อะไรคือความแตกต่างระหว่างลำดับ middleware และลำดับการทำงานสำหรับ response?
Middleware ประมวลผล request จากบนลงล่างผ่าน MIDDLEWARE แต่ประมวลผล response จากล่างขึ้นบน ลองนึกภาพเหมือนหัวหอม: middleware นอกสุดเป็นตัวแรกที่เห็น request และเป็นตัวสุดท้ายที่เห็น response
ถ: Middleware short-circuiting ทำงานอย่างไร?
เมื่อ middleware ส่งคืน HttpResponse แทนที่จะเรียก get_response() request จะไม่ไปถึง middleware ด้านในหรือ view response ยังคงผ่าน middleware ด้านนอกทั้งหมดในทางกลับ พฤติกรรมนี้แตกต่างจากการตั้งค่า MIDDLEWARE_CLASSES แบบ legacy ซึ่ง method process_response ทั้งหมดจะทำงานโดยไม่คำนึงถึง short-circuiting
ถ: เมื่อไหร่ middleware ควรเข้าถึง request.user?
หลังจาก AuthenticationMiddleware ทำงานแล้วเท่านั้น การเข้าถึง request.user ก่อนที่ authentication middleware จะแนบมันจะทำให้เกิด AttributeError วาง custom middleware ที่ต้องการข้อมูลผู้ใช้หลัง AuthenticationMiddleware ในรายการ MIDDLEWARE
ถ: จะทำให้ middleware รองรับ async ได้อย่างไร?
ตั้งค่า async_capable = True บน middleware class และ implement __call__ เป็น async method โดยใช้ await get_response(request) สำหรับ middleware ที่ต้องทำงานในทั้งบริบท sync และ async ให้ใช้ @sync_and_async_middleware และตรวจสอบ iscoroutinefunction(get_response) เพื่อแยกสาขาตามความเหมาะสม
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
การเชี่ยวชาญ Django Middleware สำหรับ Production และการสัมภาษณ์
Django middleware ควบคุม cross-cutting concerns ที่ครอบคลุมทั้ง lifecycle ของ request ประเด็นสำคัญในการทำงานกับ middleware ใน Django 6.0:
- Middleware ทำงานในรูปแบบ onion: request ไหลจากบนลงล่าง response ไหลจากล่างขึ้นบน
- ตั้งค่า
async_capable = Trueสำหรับ async-native middleware ใน deployment ASGI เพื่อหลีกเลี่ยง overhead ของ thread pool - ใช้
process_viewเพื่อตรวจสอบ metadata ของ view ก่อนการทำงาน ใช้process_exceptionสำหรับ logging error แบบรวมศูนย์ - Request ID middleware ช่วยให้สามารถเชื่อมโยง log ข้าม service แบบกระจาย ซึ่งเป็นข้อกำหนดทั่วไปใน production
LoginRequiredMiddlewareกลับด้านรูปแบบการยืนยันตัวตน: views ได้รับการป้องกันโดยค่าเริ่มต้น views สาธารณะได้รับการยกเว้นอย่างชัดเจนdjango.utils.deprecation.MiddlewareMixinที่ deprecated จะย้ายไปยังdjango.middleware.MiddlewareMixinใน Django 6.2- ลำดับ middleware กำหนดว่า component ใดจะเห็น
request.userข้อมูล session และ attribute อื่นๆ ของ request
สำหรับการครอบคลุมเชิงลึกเกี่ยวกับรูปแบบ Django ORM และ REST framework โปรดดูคู่มือการปรับปรุง Django ORM
คุณหาบั๊กใน Django เจอไหม
โค้ดจริงหนึ่งชิ้น บั๊กที่ซ่อนอยู่หนึ่งจุด วันละหนึ่งครั้ง ลองได้โดยไม่ต้องมีบัญชี

เขียนโดย
Anthony Fillion-Mailletผู้ก่อตั้ง SharpSkill
เป็นนักพัฒนาฟูลสแตกมากว่า 10 ปี ดูแล SharpSkill และรับผิดชอบทุกสิ่งที่เผยแพร่ที่นี่
อัปเดตเมื่อ 18 กันยายน 2569
แท็ก
แชร์
บทความที่เกี่ยวข้อง

Django 5.2 Custom Middleware และ Signal Handling: คู่มือเตรียมสัมภาษณ์เชิงเทคนิค
คู่มือเชิงลึก Django 5.2 custom middleware และ signal handling สำหรับการสัมภาษณ์เชิงเทคนิค ครอบคลุม request pipeline, async middleware, post_save, pre_save, custom signals และแนวทางปฏิบัติที่ดีสำหรับ production

คำถามสัมภาษณ์งาน Django: ORM, Middleware และ DRF เจาะลึก
คำถามสัมภาษณ์งาน Django ครอบคลุมการปรับแต่ง ORM ด้วย select_related, prefetch_related และโหมด FETCH_PEERS ของ Django 6.1, สถาปัตยกรรม middleware, ประสิทธิภาพ serializer ของ Django REST Framework, permissions และรูปแบบ pagination

Django 6.0 ในปี 2026: Composite Primary Key, Background Task และคำถามสัมภาษณ์งาน
คู่มือฉบับสมบูรณ์เกี่ยวกับ Django 6.0 ครอบคลุม composite primary key, framework background task ในตัว, template partial, CSP middleware พร้อมตัวอย่างโค้ดจริงและการเตรียมตัวสัมภาษณ์