通俗地说 ,中间件是充当程序或系统两个部分之间的桥梁,使它们之间的通信成为可能。在技术术语 中,中间件是Django的请求/响应处理的钩子框架。它是一个轻量级的低级“插件”系统,用于全局更改 Django 的输入或输出。每个中间件组件负责执行某些特定功能。
1.2 中间件如何工作?
中间件middleware 就是view 执行前、执行后的回调函数, 流程可以简单地描述如下
当用户向你的 Django 应用程序发出Request请求时,它首先通过中间件管道,然后再到达你的视图函数。请求按照在应用程序的中间件设置中定义中间件类的顺序进行处理。
管道中的每个中间件类负责处理特定任务或向请求处理添加功能。中间件类可以:
视图函数发出Response后,会将Response发送给中间件层, 经过中间件处理完成后,再发送给WSGI, 返还给用户。
Response响应处理
一旦请求到达视图函数,它就会被处理,并生成响应。然后,响应以相反的顺序流回中间件管道。每个中间件类都可以修改响应、添加标头或执行其他任务,然后再将其传递给管道中的上一个中间件类。
在此阶段,中间件类应处理响应对象并返回它,从而允许管道中的前一个中间件类进一步处理响应。
1.3 中间件的应用场景:
Django 中有两种类型的中间件:
内置中间件
自定义中间件
内置中间件,当你创建项目时,Django 默认提供内置中间件。您可以在项目的 settings.py 文件中检查默认中间件。
自定义中间件 — 您可以编写自己的中间件,该中间件可以在整个项目中使用。
自定义中间件你首先要在app所属目录下新建一个文件middleware.py
(或任何您喜欢的文件名), 添加好编写的中间件代码,然后在项目settings.py
中把它添加到MIDDLEWARE
列表进行注册,添加时一定要注意顺序。
您可以将中间件编写为函数或实例可调用的类。
在middleware.py中输入如下代码
import time
def timeit_middleware(get_response):
def middleware(request):
start = time.time()
response = get_response(request)
end = time.time()
print("请求花费时间: {}秒".format(end - start))
return response
return middleware
在项目 settings.py 中注册中间件
MIDDLEWARE = [
....
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'blog.middleware.timeit_middleware', # 新增
]
response = get_response(request) 是分界面,在此之前的代码执行完成后, 按顺序执行下一个中间件,如无,则跳到view。
我们现在以类来编写一个名为LoginRequiredMiddleware
的中间件,实现全站要求登录,但是登录页面和开放白名单上的urls除外。代码如下所示:
from django.shortcuts import redirect
from django.conf import settings
class LoginRequiredMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.login_url = settings.LOGIN_URL
# 开放白名单,比如['/login/', '/admin/']
self.open_urls = [self.login_url] + getattr(settings, 'OPEN_URLS', [])
def __call__(self, request):
if not request.user.is_authenticated and request.path_info not in self.open_urls:
return redirect(self.login_url + '?next=' + request.get_full_path())
response = self.get_response(request)
return response
注册中间件
MIDDLEWARE = [
'blog.middleware.simple_middleware',
'blog.middleware.LoginRequiredMiddleware', # new middleware class
]
LOGIN_URL = "/admin/login/"
OPEN_URLS = ["/admin/"]
Django还提供了其它的中间件钩子函数,分别在执行视图函数,处理异常和进行模板渲染时调用。
函数中间件如何使用钩子函数:
from django.http import HttpResponse
def timeit_middleware(get_response):
def middleware(request):
response = get_response(request)
return response
def process_view(request, view_func, view_args, view_kwargs)
return None or HttpResponse(xx)
def process_exception(self, request, exception):
return None or HttpResponse(xx)
def process_template_response(self, request, response)
return ...
middleware.process_view = process_view
middleware.process_exception = process_exception
middleware.process_template_response = process_template_response
return middleware
以类的方 法实现
class MyClassMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_view(request, view_func, view_args, view_kwargs)
return None or HttpResponse(xx)
def process_exception(self, request, exception):
return None or HttpResponse(xx)
# 例子: 打印出异常
return HttpResponse(<h1>str(exception)</h1)
# 该方法仅对TemplateResponse输入有用,对render方法失效
def process_template_response(self, request, response)
response.context_data['title'] = 'New title'
return response
除了前面章节示例中的检查用户是否登录的中间件外,下面列出几个常见的自定中间件,实际项目中可以在此基础上进行扩展。
此中间件记录每个传入请求的详细信息,例如请求方法、路径和客户端 IP 地址。这对于调试和监视目的非常有用。
import logging
logger = logging.getLogger(__name__)
class RequestLoggingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
logger.info(f"Request: {request.method} {request.path} from {request.META['REMOTE_ADDR']}")
response = self.get_response(request)
return response
此中间件检查应用程序是否处于维护模式,并为所有请求(来自特定 IP 地址的请求除外)返回自定义维护页面。
from django.http import HttpResponse
from django.template import loader
class MaintenanceModeMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
maintenance_mode = False # Set this to True when maintenance mode is enabled
allowed_ips = ['127.0.0.1'] # List of allowed IP addresses
if maintenance_mode and request.META['REMOTE_ADDR'] not in allowed_ips:
template = loader.get_template('maintenance.html')
return HttpResponse(template.render(), content_type='text/html', status=503)
response = self.get_response(request)
return response
此中间件检查请求在标头中是否包含有效的 API 密钥,如果密钥丢失或无效,则返回 HTTP 401 未授权响应。
from django.http import JsonResponse
class ApiKeyAuthenticationMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
api_key = request.META.get('HTTP_X_API_KEY')
valid_api_keys = ['your_valid_api_key'] # Replace this with your list of valid API keys
if not api_key or api_key not in valid_api_keys:
return JsonResponse({'error': 'Unauthorized'}, status=401)
response = self.get_response(request)
return response
此中间件捕获在请求/响应周期中引发的异常并记录错误,以 JSON 格式返回自定义错误响应。
import logging
logger = logging.getLogger(__name__)
class ExceptionHandlingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
response = self.get_response(request)
except Exception as e:
logger.exception(e)
return JsonResponse({'error': 'An unexpected error occurred'}, status=500)
return response
中间件的顺序很重要。
中间件只需要从类对象扩展。
自定义中间件可以使用钩子函数来满足不同阶段的处理要求。