一、全局异常处理
1.APIView执行流程
- 去除csrf认证
- 包装新的request对象
- 执行三大认证:认证、权限、频率(三大认证跑完才执行视图方法)
- 统一异常处理:三大认证、视图方法抛出的异常都会被捕获
2.DRF原生异常处理源码逻辑
配置项:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
}
-
exception_handler(exc, context):DRF内置异常函数 - 只处理 DRF自己的异常(
APIException及其子类:ValidationError、PermissionDenied、AuthenticationFailed等) - 如果是普通Python异常(除零、索引越界等),函数返回
None,DRF不会处理,直接报服务器错误
context上下文字典内容
{
'view': 当前视图类对象,
'args': 位置参数,
'kwargs': 关键字参数,
'request': 当前请求对象
}
3.自定义全局异常处理器
目标:无论什么异常,返回前端统一json格式
# exception.py
import time
from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework.exceptions import ValidationError,Throttled,AuthenticationFailed,APIException
def common_exception_handler(exc, context):
if isinstance(exc, ValidationError):
msg = exc.detail
data={'code':'103','msg':msg}
elif isinstance(exc, AuthenticationFailed):
msg= exc.detail
data = {'code': '104', 'msg': msg}
elif isinstance(exc, Throttled):
msg = exc.detail
data = {'code': '105', 'msg': msg}
elif isinstance(exc, IndexError) :
data={'code': '106', 'msg': "越界了"}
elif isinstance(exc, ZeroDivisionError) :
data={'code': '107', 'msg': "不能除以0"}
elif isinstance(exc, APIException):
msg = exc.detail
data = {'code': '999', 'msg': msg}
else:
# python原生异常
data = {'code': '888', 'msg': "未知错误,请联系系统管理员"}
# 记录错误日志信息
request=context.get('request')
view=context.get('view')
print(f'''程序错误:
错误原因是【{str(exc)}】
时间是:【{time.time()}】
请求地址是:【{request.get_full_path()}】
请求方式是:【{request.method}】
用户是:【{request.user.username or '未登录用户'}】
出错的视图类是:【{str(view)}】''')
return Response(data)
在settings配置启用自定义异常:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'app01.exception.common_exception_handler',
}
前端如果收到不是约定格式的json,说明全局异常函数内部出错。
二、接口文档
公司写接口文档几种方式
- Word/Markdown文档
- ShowDoc
- YApi(百度开源,常用)
- 自研文档平台
- 自动生成接口文档
DRF自动生成文档工具
-
coreapi:老工具 drf‑yasg-
drf‑spectacular(现在更推荐)
FastAPI框架自带接口文档
coreapi简单使用
- 安装
pip install coreapi
- 路由注册
from rest_framework.documentation import include_docs_urls
urlpatterns = [
path('docs/', include_docs_urls(title='智慧社区项目')),
]
- settings配置
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}
- 书写注释
- 视图类写类注释、方法写注释
- 序列化器字段写
help_text说明
- 访问地址:
[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
三、JWT Json Web Token
1.作用
前后端分离项目做登录认证,不在服务端存储登录信息,替代session。
session:登录信息保存在后端服务器;JWT全部信息放在token字符串,交给前端保存。
2.JWT结构(三段,.分隔,每段做base64编码)
头部.载荷.签名
- 头部(header):加密算法、token类型,基本固定
-
载荷(payload):存业务数据:用户id、用户名、过期时间
exp;⚠只是编码,不是加密,可以解码看到内容 - 签名(signature):头部+载荷 + 服务器密钥做签名,防止数据被篡改
签发流程(登录接口)
- 用户账号密码校验成功
- 组装头部、载荷,base64编码
- 头部+载荷用密钥计算签名
- 拼接
头.载荷.签名得到token字符串返回前端 - 前端保存token(localStorage、cookie、小程序存储)
认证流程(需要登录的接口)
- 前端请求头携带token
- 后端拆分三段,拿【头部+载荷】+服务器密钥重新算签名
- 和token携带的签名对比
- ✅相等:没有被篡改,再判断是否过期;取出payload里面user_id获取用户
- ❌不相等:token被篡改,认证失败
重点:载荷可以被base64解码查看内容;但是签名不能伪造,因为密钥只在后端。
3.base64编码
- base64是编码,不是加密,可以反向解码
- 长度必须是4的倍数,不足用
=补齐占位 - 用途:JWT编码、网络传输二进制(图片转base64字符串)
import base64
import json
# 编码
user_dic={'name':'lqz','age':19,'user_id':99}
user_str=json.dumps(user_dic)
res=base64.b64encode(user_str.encode('utf-8'))
# 解码
base64.b64decode(res)
四、课后作业梳理
- 练习coreapi自动生成接口文档
-
手写简易JWT签发 + 自定义认证类(不使用simple‑jwt库)
- 登录接口:手动拼接头、载荷、签名,返回token
- 自定义认证类:解析请求头token,校验签名、校验过期时间,得到登录用户
- 完善全局异常处理,统一返回格式
- 了解YApi接口管理平台
手写简易JWT逻辑思路(作业)
登录签发token
- 头部字典base64编码
- 载荷字典,带上
user_id、exp过期时间戳,base64编码 - 头部字符串+载荷字符串 + 后端密钥做md5签名,签名base64编码
- 拼接
头.载荷.签名返回给前端
{
"code":100,
"msg":"登录成功",
"token":"xxx.xxx.xxx"
}
自定义认证类
- 从
request.META取出请求头携带的token - 按
.分割三部分:header_str、payload_str、sign_old - 使用header_str+payload_str +密钥,重新计算出新签名
- 对比新签名 和 sign_old:不一致直接抛异常token非法
- base64解码payload,拿到exp过期时间,判断时间戳是否过期,过期抛异常
- 从payload取出user_id查询用户,返回(user,token)
补充记忆点(面试)
- DRF全局异常:只会处理DRF的APIException;普通Python异常需要我们自己捕获
- JWT的payload是base64编码,不是加密,不要放密码等敏感信息
- JWT签名依靠后端密钥,密钥不能泄露;篡改payload,签名校验直接失败
- base64可以双向编解码,不是加密算法