一、Django中间件
1.1 介绍
Django中的中间件是一个轻量级、底层的插件系统,介于request与response处理之间的一道处理过程(用来处理特定业务的请求和响应)。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。
我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。
请求—中间件—响应:
1.1.1 中间件配置
定义好中间件后,需要在settings.py 文件中添加注册中间件
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware', # 安全中间件'django.contrib.sessions.middleware.SessionMiddleware', # 会话中间件'corsheaders.middleware.CorsMiddleware', # 跨域请求中间件'django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware', # 防伪造跨域请求中间件'django.contrib.auth.middleware.AuthenticationMiddleware', # 授权中间件'django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]
【拓展】HTTP请求方法:
-
GET: 查询
-
POST: 添加或修改数据
-
PUT : 修改数据
-
DELETE: 删除数据
-
OPTIONS: 探测服务器是否支持跨域
Access-Control-Allow
[-Origin|Headers|Methods|Credentials]访问-控制-允许-[源|头|方法|认证]
1.1.2 中间件的钩子函数(生命周期):
process_request: 请求到达时,未到路由
process_view: 转到view处理函数之前
process_exception: 在view处理函数中抛出异常时
process_response: 在View响应之后,向客户返回(输出)之前
process_template_response: 响应之前,渲染模板之后
1.1.3 自定义中间件类
from django.utils.deprecation import MiddlewareMixinclass AuthUserMiddleware(MiddlewareMixin):def process_request(self, request):passdef process_view(self, request, view, *args, **kwargs):passdef process_exception(self, request, exception):passdef process_response(self, request, response):return response
1.1.4 通过中间件实现的功能
- 请求黑名单: 1秒内允许2次请求(正常),非正常,则在process_request()方法直接返回
- 提取授权用户的信息和权限验证: 提前配置请求路径和用户权限
- 异常的友好提示
- 缓存数据(减少数据库的交互次数): process_response() 对response对象进行序列化和缓存。
- 记录日志(logging 模块)
1.2 线程方式实现缓存
import time
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
import threadingcached = {}# 过期缓存监控的线程
class ExpireThread(threading.Thread):def clear(self, path, max_age):time.sleep(max_age)print('--checked--', path, cached.keys())if path in cached:cached.pop(path)def run(self):while True:time.sleep(1)print('--check--', cached.keys())for path, resp_max_age in cached.items():threading.Thread(target=self.clear,args=(path, resp_max_age[-1])).start()class MyMiddleware(MiddlewareMixin):def process_request(self, request):print('--process_request---', request.path)# 判断当前请求路径是否在缓存中# 如果存在则,直接返回响应if request.path in cached:return cached[request.path][0]def process_view(self, request, view, *args, **kwargs):# args 表示 view函数的位置参数# kwargs 表示 view函数的关键参数print('---process_view---', args)def process_exception(self, request, exception):print(request.path, dir(exception))print('----process_exception----', exception)return HttpResponse('<h3>Error Page 500: %s</h3>' % str(exception))def process_response(self, request, response):print('---process_response---')if request.path not in cached:# 存入缓存(内存)print('---存入缓存---')cached[request.path] = (response, 3)print('---缓存---', cached)return responseExpireThread().start()
注册中间件, 在settings.py文件中,修改MIDDLEWARE,如下所示:
MIDDLEWARE = [...'apiapp.middleware.MyMiddleware','django.middleware.common.CommonMiddleware',...
]
1.3 基于Redis实现缓存页面
from utils.caches import save_response, get_response, has_response
import pickle
import base64class RedisCacheMiddleware(MiddlewareMixin):def process_request(self, request):print('--process_request---', request.path)# 判断当前请求路径是否在缓存中# 如果存在则,直接返回响应if has_response(request.path):resp_base_str = get_response(request.path)resp_bytes = base64.b32decode(resp_base_str.encode())response = pickle.loads(resp_bytes)return responsedef process_response(self, request, response):print('---process_response---')# 存入缓存(内存)if not has_response(request.path):resp_bytes = pickle.dumps(response) # 将python的任意对象序列化字段码# b'QABWGZDKMFXGO3ZONB2HI4BOOJSXG4DPNZZWKCSIOR2HAUTFONYG63TTMUFHCABJQFYQC7LRAIUFQCAAAAAF'resp_base64_str = base64.b32encode(resp_bytes).decode()save_response(request.path, resp_base64_str, 3)return response
二、日志模块
Python的日志模块是: logging
import logging
2.1 日志的四大部分
日志是用于记录(Logger)程序中的一些重要信息的,记录的信息按等级(Level)交给特定的处理器Handler按一定的格式(Formatter)进行处理(打印、文件保存、上传网络和发送邮件等)。
2.1.1 日志记录器 Logger
- 记录日志信息的唯一对象, 根据不同的信息等级使用不同的记录方法
- x.debug() 记录调试信息,logging.DEBUG (10)
- x.info() 记录普通信息, logging.INFO (20)
- x.warning() 记录警告信息, loggin.WARNING (30)
- x.error() 记录错误信息, logging.ERROR (40)
- x.critical() 记录严重错误的信息, loggin.CRITICAL (50)
- 创建或获取记录器: logging.getLogger(name) 如果name为空,则默认为root。
- 记录器可以添加处理器和过滤器: x.addHandler(Handler())、 x.addFilter(Filter() )
2.1.2 日志处理器 Handler
作用: 将记录器记录的相关日志信息,进行特定业务的处理,如写入文件的FileHandler、发送邮件的 SMTPHandler、上传远程服务器的HTTPHandler等。
【注意】处理器可以设置日志格式化 Formatter: h.setFormatter(Formatter())
- StreamHandler 流处理器, 控制台打印的
- FileHandler 文件处理器,将日志消息写入到文件中
- HTTPHandler 网络处理器,将日志消息上传到服务器
- SMTPHandler 邮件处理器,将日志消息发送到指定邮件中。
日志记录器和处理器的关系:
一个记录器可以添加多个处理器。
logger.addHandler(handler1)
logger.addHandler(handler2)
logger = logging.getLogger("network")
logger.setLevel(logging.INFO) handler1 = StreamHandler()
handler1.setLevel(logging.INFO)handler2 = FileHandler("network.log")
handler1.setLevel(logging.WARNING)logger.addHandler(handler1)
logger.addHandler(handler2)
2.1.3 日志格式化 Formatter
作用: 将记录日志信息进行格式化(可以选择重要的信息、或者自定义格式的信息)
经典的日志格式:
[ %(asctime)s - %(levelname)s < line %(lineno)s at %(pathname)s > ] %(message)s
如果给定的日志格式中变量不能完成业务需求时,可以自定义:
[ %(asctime)s - %(username)s: %(ip)s < %(action)s > ] %(message)s
以上username、ip和action都属于自定义的格式变量,在记录器记录信息时指定extra 参数, 它是一个字典类型,key即为扩展的变量名,value即是它的值。
日志的格式:
| %(name)s Name of the logger (logging channel) 记录器的名称| %(levelno)s Numeric logging level for the message (DEBUG, INFO,| WARNING, ERROR, CRITICAL) 数字形式的日志记录级别| %(levelname)s Text logging level for the message ("DEBUG", "INFO",| "WARNING", "ERROR", "CRITICAL") 日志记录级别的文本名称| %(pathname)s Full pathname of the source file where the logging| call was issued (if available) 执行日志记录调用的源文件的路径名称| %(filename)s Filename portion of pathname 执行日志记录调用的源文件的文件名称| %(module)s Module (name portion of filename) 执行日志记录调用的模块名称| %(lineno)d Source line number where the logging call was issued| (if available) 执行日志记录调用的行号| %(funcName)s Function name 执行日志记录调用的函数名称| %(created)f Time when the LogRecord was created (time.time()| return value) 执行日志记录的时间| %(asctime)s Textual time when the LogRecord was created 日期和时间| %(msecs)d Millisecond portion of the creation time 毫秒部分| %(relativeCreated)d Time in milliseconds when the LogRecord was created,| relative to the time the logging module was loaded| (typically at application startup time)| %(thread)d Thread ID (if available) 线程ID| %(threadName)s Thread name (if available) 线程名称| %(process)d Process ID (if available) 进程ID| %(message)s The result of record.getMessage(), computed just as| the record is emitted 记录的消息
2.1.4 日志过滤器 Filter
作用: 过滤一些敏感的记录日志信息。
2.2 日志的应用
2.2.1 标准使用
#!/usr/bin/python3
import logging
from logging import StreamHandler, FileHandler, Formatter, Filterclass MsgFilter(Filter):def filter(self, record): # record记录对象# print(dir(record)) # 查看记录器里面的方法return record.msg.find('混沌') == -1logger = logging.getLogger('middleware')
# 设置记录器的信息等级:DEBUG -> INFO -> WARNING -> ERROR -> CRITICAL
logger.setLevel(logging.INFO) # 记录器可以记录INFO及以上等级信息# 添加日志过滤器
logger.addFilter(MsgFilter())# 创建日志处理器
handler1 = StreamHandler() # 流处理器,标准输出
handler1.setLevel(logging.INFO)
handler1.setFormatter(Formatter(fmt='[ %(asctime)s - %(levelname)s] %(message)s',datefmt='%Y-%m-%d %H:%M:%S'
))# 向记录器添加处理器
logger.addHandler(handler1)handler2 = FileHandler('error.log', encoding='utf-8')
handler2.setLevel(logging.ERROR)
handler2.setFormatter(Formatter(fmt="[ %(asctime)s - %(levelname)s < line %(lineno)s at %(pathname)s > ] %(message)s",datefmt='%Y-%m-%d %H:%M:%S'
))
logger.addHandler(handler2)if __name__ == '__main__':# x = logging.getLogger('middleware') # 获取middleware的记录器logging.getLogger('middleware').debug('中午吃拉面')logging.getLogger('middleware').info('中午吃木桶饭')logging.getLogger('middleware').warning('中午吃凉皮')logging.getLogger('middleware').error('中午吃粥和夹馍')logging.getLogger('middleware').critical('下午吃混沌')
2.2.2 扩展格式化的使用
mylogger = logging.getLogger('sys_action')
mylogger.setLevel(logging.INFO)myhandler = StreamHandler()
myhandler.setLevel(logging.INFO)
myhandler.setFormatter(Formatter(fmt="[ %(asctime)s - %(username)s: %(ip)s < %(action)s > ] %(message)s",datefmt='%Y-%m-%d %H:%M:%S'
))mylogger.addHandler(myhandler)if __name__ == '__main__':extra_info = {'username': 'admin','ip': '10.36.172.110','action': 'del category'}logging.getLogger('sys_action').debug('中午吃拉面', extra=extra_info)logging.getLogger('sys_action').info('中午吃木桶饭', extra=extra_info)logging.getLogger('sys_action').warning('中午吃凉皮',extra=extra_info)logging.getLogger('sys_action').error('中午吃粥和夹馍', extra=extra_info)logging.getLogger('sys_action').critical('下午吃混沌', extra=extra_info)