DRF学习07-全局异常、接口文档、JWT 基础

一、全局异常处理

1.APIView执行流程

  1. 去除csrf认证
  2. 包装新的request对象
  3. 执行三大认证:认证、权限、频率(三大认证跑完才执行视图方法)
  4. 统一异常处理:三大认证、视图方法抛出的异常都会被捕获

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,说明全局异常函数内部出错。


二、接口文档

公司写接口文档几种方式

  1. Word/Markdown文档
  2. ShowDoc
  3. YApi(百度开源,常用)
  4. 自研文档平台
  5. 自动生成接口文档

DRF自动生成文档工具

  • coreapi:老工具
  • drf‑yasg
  • drf‑spectacular(现在更推荐)

FastAPI框架自带接口文档

coreapi简单使用

  1. 安装
pip install coreapi
  1. 路由注册
from rest_framework.documentation import include_docs_urls
urlpatterns = [
    path('docs/', include_docs_urls(title='智慧社区项目')),
]
  1. settings配置
REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}
  1. 书写注释
  • 视图类写类注释、方法写注释
  • 序列化器字段写help_text说明
  1. 访问地址:[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编码)

头部.载荷.签名

  1. 头部(header):加密算法、token类型,基本固定
  2. 载荷(payload):存业务数据:用户id、用户名、过期时间exp;⚠只是编码,不是加密,可以解码看到内容
  3. 签名(signature):头部+载荷 + 服务器密钥做签名,防止数据被篡改

签发流程(登录接口)

  1. 用户账号密码校验成功
  2. 组装头部、载荷,base64编码
  3. 头部+载荷用密钥计算签名
  4. 拼接 头.载荷.签名 得到token字符串返回前端
  5. 前端保存token(localStorage、cookie、小程序存储)

认证流程(需要登录的接口)

  1. 前端请求头携带token
  2. 后端拆分三段,拿【头部+载荷】+服务器密钥重新算签名
  3. 和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)

四、课后作业梳理

  1. 练习coreapi自动生成接口文档
  2. 手写简易JWT签发 + 自定义认证类(不使用simple‑jwt库)
    • 登录接口:手动拼接头、载荷、签名,返回token
    • 自定义认证类:解析请求头token,校验签名、校验过期时间,得到登录用户
  3. 完善全局异常处理,统一返回格式
  4. 了解YApi接口管理平台

手写简易JWT逻辑思路(作业)

登录签发token

  1. 头部字典base64编码
  2. 载荷字典,带上user_id、exp过期时间戳,base64编码
  3. 头部字符串+载荷字符串 + 后端密钥做md5签名,签名base64编码
  4. 拼接 头.载荷.签名 返回给前端
{
    "code":100,
    "msg":"登录成功",
    "token":"xxx.xxx.xxx"
}

自定义认证类

  1. 从request.META取出请求头携带的token
  2. 按.分割三部分:header_str、payload_str、sign_old
  3. 使用header_str+payload_str +密钥,重新计算出新签名
  4. 对比新签名 和 sign_old:不一致直接抛异常token非法
  5. base64解码payload,拿到exp过期时间,判断时间戳是否过期,过期抛异常
  6. 从payload取出user_id查询用户,返回(user,token)

补充记忆点(面试)

  1. DRF全局异常:只会处理DRF的APIException;普通Python异常需要我们自己捕获
  2. JWT的payload是base64编码,不是加密,不要放密码等敏感信息
  3. JWT签名依靠后端密钥,密钥不能泄露;篡改payload,签名校验直接失败
  4. base64可以双向编解码,不是加密算法
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容