drf-异常处理、接口文档

一 异常处理 Exceptions

REST framework提供了异常处理,我们可以自定义异常处理函数。

对于前端来讲,后端即便报错,也要返回统一的格式,前端便于处理

{code:999,msg:'系统异常,请联系系统管理员'}

1.1 异常源码

-1 settings中的
    # Exception handling
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
	# drf只要出了异常,就会执行上面的
-2 查看异常的源码,入口,APIView的dispatch方法中
    def dispatch(self, request, *args, **kwargs):
        try:
            self.initial(request, *args, **kwargs)
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
        except Exception as exc:
            response = self.handle_exception(exc)  # 异常处理***
        return self.response
    
-3 APIView的handle_exception方法
    def handle_exception(self, exc):
        if isinstance(exc, (exceptions.NotAuthenticated,
                            exceptions.AuthenticationFailed)):
            auth_header = self.get_authenticate_header(self.request)

            if auth_header:
                exc.auth_header = auth_header
            else:
                exc.status_code = status.HTTP_403_FORBIDDEN

        exception_handler = self.get_exception_handler()

        context = self.get_exception_handler_context()
        response = exception_handler(exc, context)  # ***
        response.exception = True
        return response
    
-4 异常函数:exception_handler(位置:rest_framework.views.exception_handler)
def exception_handler(exc, context):
    if isinstance(exc, exceptions.APIException):
        # 判断这个错误对象exc是否是APIException的对象
        
        if getattr(exc, 'auth_header', None):
            headers['WWW-Authenticate'] = exc.auth_header
        if getattr(exc, 'wait', None):
            headers['Retry-After'] = '%d' % exc.wait

        if isinstance(exc.detail, (list, dict)):
            # 如果exc.detail是列表或者字典,直接返回这个数据
            data = exc.detail
        else:  # 如果是其他格式,组装成字典形式返回
            data =  {'detail': exc.detail}

        set_rollback()
        return Response(data, status=exc.status_code, headers=headers)

    return None

总结

# 只要三大认证,视图类的方法出了异常,都会执行一个函数:rest_framework.views import exception_handler

### 注意:exception_handler
    # 如果异常对象是drf的APIException对象,就会返回Response
    # exception_handler只处理了drf的异常,其它的异常需要我们自己处理
    # 如果异常对象不是drf的APIException对象,就会返回None

1.2 自定义异常

exceptions.py

# 只要三大认证,视图类的方法出了异常,都会执行一个函数:rest_framework.views import exception_handler,这个函数是写死的,固定的。所以我们要重写这个函数

# 处理全局异常,返回统一格式
步骤:
    -1 重写common_exception_handler()函数
    -2 返回自己定义的格式。统一返回格式
    -2 在配置文件中设置
  

from rest_framework.response import Response
from rest_framework.views import exception_handler

# 如果异常对象是drf的APIException对象,就会返回Response
# exception_handler只处理了drf的异常,其它的异常(返回None,页面会直接报错)需要我们自己处理
# 如果异常对象不是drf的APIException对象,就会返回None
# isinstance()   判断一个对象是不是某个类的对象  isinstance(对象,类)
# issubclass()   判断一个类,是不是另一个类的子类


def common_exception_handler(exc, context):
    # 只要走到这里,一定出异常了,我们正常的项目要记录日志(后面讲)
    # 两种可能:一个是Response对象,一个是None
    res = exception_handler(exc, context)
    if res:
        # 说明有值是drf的异常,它处理了
        if isinstance(res.data, dict):
            # 显示drf异常的详细信息
            detail = res.data.get('detail')
        else:
            detail = res.data
        return Response({'code': 998, 'msg': detail})
    else:
        # 说明是其它异常None,它没有处理
        # return Response({'code': 999, 'msg': '系统异常,请联系系统管理员'})
        # 显示错误具体信息
        return Response({'code': 999, 'msg': str(exc)})

视图类

########### 1 全局异常处理
# drf只要出了异常,就会执行:'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
# 但是源码是不能改的,我们可以自定义一个函数,配置一下,以后出了异常,执行我们自己的函数
from rest_framework.views import exception_handler
from rest_framework.exceptions import APIException
from rest_framework.views import APIView

class BookView(APIView):
    # /books/?ordering=-price&name=红楼梦
    def get(self, request):
        ordering = request.query_params.get('ordering')
        name = request.query_params.get('name')
        # 异常测试
        # 1.drf异常
        # raise APIException('我爱你')  # 前端能看到这个中文
        # 2.其他异常
        # raise Exception('我爱你')
        # 3.
        # l = [1, 2, 3]
        # print(l[9])
        book_list = Book.objects.all()
        if ordering:
            book_list = book_list.order_by(ordering)
        if name:
            book_list = book_list.filter(name__contains=name)
        ser = BookSerialzier(instance=book_list, many=True)
        return Response(ser.data)

设置配置文件

### 配置文件
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'app01.excepitons.common_exception_handler',
}

1.3 REST framework定义的异常

  • APIException 所有异常的父类
  • ParseError 解析错误
  • AuthenticationFailed 认证失败
  • NotAuthenticated 尚未认证
  • PermissionDenied 权限决绝
  • NotFound 未找到
  • MethodNotAllowed 请求方式不支持
  • NotAcceptable 要获取的数据格式不支持
  • Throttled 超过限流次数
  • ValidationError 校验失败

也就是说,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。

二 接口文档

2.1 介绍

# 后端把接口写好后,再写一个接口文档
    -登录接口
    -注册接口
    -查询所有图书带过滤接口
# 前端人员需要根据接口文档,进行前端开发

# 前后端需要做对接----> 对接第一个东西就是这个接口文档---> 前端照着接口文档开发

# 公司3个人,每个人开发了10个接口,3个人都要同时写接口文档

# 接口文档的编写形式
    -1 world,md,编写,大家都可以操作,写完放在git,公司的文档管理平台上
    -2 第三方的接口文档平台(收费)
    	https://www.showdoc.com.cn/
    -3 公司自己开发接口文档平台
    -4 公司使用开源的接口文档平台,搭建
    	-YAPI:百度开源的
        -https://zhuanlan.zhihu.com/p/366025001
        
    -5 项目自动生成接口文档
    	-coreapi
        -swagger

# swaggerAPI文档生成器: https://blog.csdn.net/qq_44614026/article/details/128710326

2.2 使用coreapi自动生成接口文档

-使用步骤:
    -1 安装:pip3 install coreapi

    -2 加一个路由
        from rest_framework.documentation import include_docs_urls	
        urlpatterns = [
            path('docs/', include_docs_urls(title='站点页面标题'))
        ]

    -3 在视图类上加注释(描述信息)
            
    -4 配置文件中配置:
        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    -5 表模型或序列化类的字段上写 help_text--->会显示在接口文档的字段介绍上
            
    -6 访问地址:
        http://127.0.0.1:8000/docs/

视图类

在视图类中添加注释

  1. 单一方法的视图,可直接使用类视图的文档字符串,如
class BookListView(generics.ListAPIView):
    """
    返回所有图书信息.
    """
    queryset = Book.objects.all()
    serializer_class = BookSerializer

2)包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如

class BookListCreateView(generics.ListCreateAPIView):
    """
    get:
    返回所有图书信息.

    post:
    新建图书.
    """
    queryset = Book.objects.all()
    serializer_class = BookSerializer

3)对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分,如

class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
    """
    list:
    返回图书列表数据

    retrieve:
    返回图书详情数据

    latest:
    返回最新的图书数据

    read:
    修改图书的阅读量
    """
    queryset = Book.objects.all()
    serializer_class = BookSerializer

路由

from django.contrib import admin
from django.urls import path, include
from app01 import views
from rest_framework.routers import SimpleRouter
from rest_framework.documentation import include_docs_urls


router = SimpleRouter()
router.register('books', views.BookInfoViewSet, 'books')

urlpatterns = [
    # 接口文档的路由
    path('docs/', include_docs_urls(title='电商项目接口文档')),

    # 两个视图函数的路由
    path('lqz/', views.BookListCreateView.as_view()),
    path('zzz/', views.BookListView.as_view()),
]

urlpatterns += router.urls

两点说明

1) 视图集ViewSet中的retrieve名称,在接口文档网站中叫做read

2)参数的Description需要在模型类或序列化器类的字段中以help_text选项定义,如:

class Student(models.Model):
    ...
    age = models.IntegerField(default=0, verbose_name='年龄', help_text='年龄')
    ...

class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
        extra_kwargs = {
            'age': {
                'required': True,
                'help_text': '年龄'
            }
        }

posted @ 2023-05-27 20:56  星空看海  阅读(136)  评论(0)    收藏  举报