1. 概述
Django提供了中间件(Middleware)来处理请求,中间件是介于Web服务器和视图之间的一组钩子函数。它们可以对请求和响应进行修改、记录日志、验证用户等操作。
中间件的实现需要定义一个Python类,类中必须实现一些钩子函数。当请求被处理时,会按照定义的顺序依次执行所有中间件的这些钩子函数。
2. 中间件的定义
中间件的定义和Django中其他的组件类似,需要在应用的middleware.py
中定义一个Python类。下面是一个简单的示例:
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
2.1 中间件类的初始化
中间件类需要实现__init__
方法,这个方法需要接收一个get_response
参数,这个参数是Django框架传入的回调函数,用来获取下一个中间件或者视图函数。
2.2 中间件的调用
中间件类的实例可以像调用普通Python函数一样调用,这是中间件的核心。当我们调用中间件实例时,实际上就是在调用__call__
方法。这个方法接收一个request
参数,返回一个response
对象。
2.3 中间件的顺序
在Django中,中间件的执行顺序由MIDDLEWARE
设置决定。这个设置是一个包含所有中间件类名的列表,按顺序依次执行。
3. 中间件钩子函数
中间件的钩子函数定义了中间件对请求和响应所做的处理。下面是Django内置的中间件钩子函数:
process_request(request): 在请求被处理之前调用
process_view(request, view_func, view_args, view_kwargs): 在视图函数被调用之前调用
process_exception(request, exception): 在视图函数抛出异常时调用
process_template_response(request, response): 在视图函数返回响应之前调用,如果响应是TemplateResponse对象
process_response(request, response): 在视图函数返回响应之后调用
这些钩子函数需要返回一个response
对象,作为后续中间件或视图函数的输入。如果返回None
,表示不做处理,跳过后续的中间件或视图函数。
3.1 process_request钩子函数
process_request钩子函数在view_func
被调用之前执行。
可以使用process_request在视图函数处理之前进行一些预处理。例如,解析请求参数、鉴权等操作。这是非常常见的一种中间件用法。
下面是一个简单的示例,假设我们要处理MyModel.objects.filter(is_public=True)
的查询,如果用户没有登录,则将is_public参数设置为True
,否则设置为False
:
from django.contrib.auth import authenticate
class AuthMiddleware:
def __init__(self, get_response):
# One-time configuration and initialization.
self.get_response = get_response
def __call__(self, request):
is_public = request.GET.get('is_public', False)
if request.user.is_authenticated:
is_public = False
request.custom_is_public = is_public
# Call the next middleware or view.
response = self.get_response(request)
return response
class MyView(View):
def get(self, request):
queryset = MyModel.objects.filter(is_public=request.custom_is_public)
# ...
在上面的代码中,使用了request.custom_is_public
这个自定义的属性来存储参数值。在AuthMiddleware
中进行鉴权判断,然后设置这个自定义属性的值,MyView
中就可以使用这个值进行查询了。
3.2 process_view钩子函数
process_view钩子函数在视图函数被调用之前执行,可以用来进行URL分发、请求参数解析、用户鉴权等操作。
下面是一个简单的示例,假设我们需要实现/api/v1/anonymous
和/api/v1/authenticated
两个接口,其中/api/v1/anonymous
接口不需要鉴权,/api/v1/authenticated
接口需要验证用户是否登录:
class AuthMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 鉴权代码 ...
return self.get_response(request)
class AnonymousView(View):
def get(self, request):
# ...
return HttpResponse('anonymous')
class AuthenticatedView(View):
def get(self, request):
# ...
return HttpResponse('authenticated')
def is_authenticated(request):
return request.user.is_authenticated
urlpatterns = [
path('api/v1/anonymous', AnonymousView.as_view()),
path('api/v1/authenticated', AuthenticatedView.as_view()),
]
MIDDLEWARE = [
AuthMiddleware,
AuthenticationMiddleware,
...
]
# Django Rest Framework的路由注册
router = DefaultRouter()
router.register(r'api/v1/anonymous', AnonymousView, basename='anonymous')
router.register(r'api/v1/authenticated', AuthenticatedView, basename='authenticated')
urlpatterns += router.urls
# 设置鉴权函数,DRF使用
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': ['v1'],
'VERSION_PARAMETER': 'api_version',
'EXCEPTION_HANDLER': 'backend.exception_handler.custom_exception_handler',
'DEFAULT_THROTTLE_RATES': {
'anon': '3600/day',
'user': '10000000/minute'
},
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle',
],
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata'
}
上面的代码中,使用AuthenticationMiddleware
进行用户鉴权,如果用户登录,则其会话保存在Request对象中。通过判断当前URL是否需要鉴权,来确定是否对请求进行拦截。
3.3 process_exception钩子函数
process_exception钩子函数在视图函数抛出异常时执行。
这个钩子函数可以用来处理异常,是否需要捕获异常、记录异常等。一般来说,对于异常的处理,需要根据实际情况进行处理。
下面是一个简单的示例,如果在处理请求时出现了异常,直接返回一个JSON格式的错误响应。这个响应可以是一个自定义的错误码、错误信息,也可以根据异常类型来判断返回什么信息。
class CustomExceptionMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
response = self.get_response(request)
except Exception as e:
response = JsonResponse({'code': -1, 'message': str(e)})
return response
3.4 process_template_response钩子函数
process_template_response钩子函数在视图函数返回响应之前调用,如果响应是TemplateResponse
对象,则可以对响应进行修改和操作。
下面是一个简单的示例,对于所有返回的TemplateResponse
对象,添加一个最后修改时间的信息。
class LastModifiedMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if isinstance(response, TemplateResponse):
response.context_data['last_modified'] = datetime.now()
return response
3.5 process_response钩子函数
process_response钩子函数在视图函数返回响应之后执行,可以对响应进行缓存、压缩、加密等操作。
下面是一个简单的示例,在所有响应头中添加一个Server
标识,用于标记当前服务的名称和版本号。
class ServerHeaderMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response['Server'] = 'MyServer/v1.0'
return response
4. 中间件的常见用法
中间件可以用来实现各种功能,下面是一些常见的中间件用法:
4.1 记录请求日志
记录每次请求的URL、方法、请求参数、响应时间等信息,用于问题排查和性能分析。
下面是一个简单的请求日志中间件示例,将请求信息输出到控制台:
class RequestLogMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start_time = time.time()
response = self.get_response(request)
end_time = time.time()
duration = end_time - start_time
print(f'[{request.method}] {request.path} {duration:.2f} seconds')
return response
4.2 认证和鉴权
在请求被处理前、处理后都可以执行一些验权的方法,这样我们就可以在它们执行的时候进行用户鉴权;如果用户无法通过鉴权,我们可以返回一个HTTP401异常。
下面是一个示例,数据存储在MongoDB中,在调用路由时进行验证:
class AuthMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.get_response = get_response
def process_request(self, request):
try:
token = request.META['HTTP_AUTHORIZATION']
data = check_auth(token)
request.user = data
except:
response = JsonResponse(data={'message': 'api key invalid'}, status=401)
return response
return None
4.3 异常处理
当视图函数抛出异常时,我们可以在中间件中进行统一的异常处理,比如记录异常日志、返回一个友好的错误信息等。
下面是一个简单的异常处理中间件示例,记录异常日志到一个文件中:
class ExceptionLogMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
response = self.get_response(request)
except Exception as e:
with open('exceptions.log', 'a') as f:
f.write(str(e))
response = HttpResponse(status=500)
return response
4.4 CORS跨域访问控制
在Web开发中出现跨域访问的问题,CORS是常用的一种解决方案。
下面是一个简单的CORS中间件示例:
class CorsMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Headers'] = '*'
return response
5. 总结
中间件是Django非常重要的一部分,它可以帮助我们实现各种功能,如:鉴权、处理异常、记录日志、跨域访问控制等。需要注意的是,中间件需要定义在MIDDLEWARE中,并按照顺序执行。