django中间件
一、中间件详细分析
1.django配置文件settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
... ...
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'pay_gateway.process_handle_middleware.ProcessHandleMiddleware'
]
2.自定义中间件middleware.py
from django.utils.deprecation import MiddlewareMixin
class ProcessHandleMiddleware(MiddlewareMixin):
"""
以下几个函数的顺序,刚好也是中间件的执行顺序,
按需在适当的位置做操作
"""
def __init__(self, *args, **kwargs):
"""
如果需要额外初始化数据则定义
"""
super(ProcessHandleMiddleware, self).__init__(*args, **kwargs)
self.start_time = time.time()
def process_request(self, request):
"""request请求一进来的预处理函数。
- 在Django接收到request之后,未解析URL到对应视图函数之前。Django向它传入相应的Request对象,以便在方法中修改。
- 若返回None,Django将继续处理这个request,执行后续的中间件, 然后调用相应的 view。
- 若返回HttpResponse对象,Django将不再执行任何除了process_response(返回浏览器的最后一层干预)以外其它的中间件
以及相应的view,Django将立即返回该HttpResponse
- 这里如果报错,后边所有的中间件都就不执行了,直接返回(由内部对error处理,报500)
"""
print("request arrive ...")
def process_view(self, request, callback, callback_args, callback_kwargs):
"""进view前的预处理函数
- 在Django执行完request预处理,路由解析完毕,确定待执行的view函数(即callback函数)之后,但在view实际执行之前
:param request: HttpRequest 对象
:param callback: Django将调用的处理request的python函数. 这是实际的函数对象本身, 而不是字符串表述的函数名
:param callback_args: 将传入view的位置参数列表,但不包括request参数(它通常是传入view的第一个参数)
:param callback_kwargs: 将传入view的关键字参数字典
:return:
- 如果返回None, Django将继续处理这个request ,执行后续的中间件, 然后调用相应的view
- 如果返回HttpResponse对象,Django将不再执行任何其它的中间件(不论种类)以及相应的view,Django会立即返回
- 这里如果报错,后边所有的中间件都就不执行了,直接返回(由内部对error处理,报500)
"""
print("before view")
def process_template_response(self):
"""template模板渲染函数
- 默认不执行,只有在view函数返回的结果对象中有render方法才会执行
- 若返回的话,会把对象内执行render方法后的返回值返回给用户(不返回view返回的对象,而是其对象内render方法的返回值)
"""
print("template response ")
def process_exception(self, request, exception):
"""只有view函数处理抛出的错误才能接到,其他的错误,这里并不能捕获
- 可做异常通知,错误日志搜集,或尝试从错误中自动恢复
- 若返回None,Django将用框架内置的异常处理机制继续处理相应request
- 若返回HttpResponse对象,Django将使用该response对象,而短路框架内置的异常处理机制,直接返回(其他异常处理便不再执行)
"""
print("process exception")
def process_response(self, request, response):
"""在view函数返回response对象之后,马上要返回给用户之前,对其进行二次处理
- 如:用gzip压缩返回数据
- 这里必须要返回HttpResponse对象!!! (可以是view返回的,也可是全新的,也可是处理过的,但必须是HttpResponse对象)
"""
print("process response ... ... ")
return response
3.注意事项:
-
process_view
在settings.py
中的MIDDLEWARE
中,后放的先执行,其他过程是先放的先执行。
-
process_response
一定要有reurn否则会报错,自定义的中间件response方法没有return,会交给下一个中间件,导致http请求中断了。
-
process_view(self, request, callback,callback_args, callback_kwargs)
方法介绍:
- (1)执行完所有中间件的
request
方法
- (2)url匹配成功
- (3)拿到视图函数的名称、参数(注意不执行),再执行
process_view()
方法(传递view函数以及相关参数,但是还是不执行)
- (4)最后去执行视图函数
4.看图分析
二、简化版,以及参数分析
class LoggerMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
print("start request".center(50, "*"))
print(request)
print(type(request))
print(dir(request))
print(request.method)
print(request.content_type)
print(request.META)
print(request.META['REMOTE_ADDR'])
print(type(request.META['REMOTE_ADDR']))
print(request.content_params)
print(request.encoding)
print(request.body)
print(str(request.body, encoding="utf-8"))
print(request.get_full_path())
print(request.get_host())
print(request.scheme)
print(request.GET)
print(type(request.GET))
print(dir(request.GET))
print(request.GET.dict())
print(request.POST)
print(type(request.POST))
print(dir(request.POST))
print(request.POST.dict())
print(request.POST.items())
print(request.POST.lists())
for k, v in request.POST.items():
print("%s=%s"%(k,v))
print(request.POST.keys())
response = self.get_response(request)
print("get response data".center(50, ">"))
print(type(response))
print(dir(response))
print(response)
print(response.status_code)
print(response.content)
print(response.items())
print(response.readable())
print(response.serialize())
return response
三、应用实例
from django.http.response import JsonResponse
class ServerException(Exception):
def __init__(self, reason, *args):
self.reason = reason
self.code = error_dict[self.reason]['code']
self.data = None
self.msg = error_dict[self.reason]["msg"] % args
def to_response(self):
return JsonResponse({
"code": self.code,
"reason": self.reason,
"data": self.data,
"msg": self.msg
})
class ProcessHandleMiddleware(MiddlewareMixin):
def __init__(self, *args, **kwargs):
super(ProcessHandleMiddleware, self).__init__(*args, **kwargs)
self.start_time = time.time()
def process_request(self, request):
"""
- 这里不能调用Response进行数据返回!
- Response或SimpleTemplateResponse等实际返回的是Response().render(),
而如果想进行渲染,那么在实例化时需要指定各种渲染类。
但Response中是没有渲染类的默认值,而在View中会取默认值给Response,
因而经过view的可以返回,自己在此位置直接调用并返回是不可以。
- HTTPResponse和JSONResponse是直接拼凑二进制数据的响应,不存在渲染操作,所以可以直接返回
"""
def process_exception(self, request, exception):
capture_exception()
if isinstance(exception, ServerException):
return exception.to_response()
def process_response(self, request, response):
"""
1.如果想实现参数封装,这里只能使用rest_framework.response.Response !!!
2.只针对接口函数的返回值做封装+日志记录
"""
if str(request.path).startswith("/open-api"):
if hasattr(response, 'data') and isinstance(response, Response):
data = response.data
response.data = {
'code': 0,
'data': data,
'reason': "SUCCESS",
'msg': '成功'
}
response._is_rendered = False
response.render()
req_data = ""
if request.method == 'GET':
req_data = request.GET.dict()
elif request.method == 'POST':
req_data = str(request.body, encoding='utf-8')
resp = ""
if hasattr(response, "content"):
resp = response.content.decode("utf-8")
end_time = time.time()
log_info = "<[REQUEST MESSAGE]>: [req_path]: %s, [mehtod]: %s, [cost_time]: %.2f s, [status]: %s, [req_data]: %s, [resp_data]: %s" % (
request.path,
request.method,
end_time - self.start_time,
response.status_code,
req_data,
resp
)
logging.info(log_info)
return response