Django中间件(Middleware)处理请求

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中,并按照顺序执行。

后端开发标签