drf之异常处理,自动生成接口文档
# 7. 异常处理 Exceptions
REST framework本身在APIView提供了异常处理,但是仅针对drf内部现有的接口开发相关的异常进行格式处理,但是开发中我们还会使用到各种的数据或者进行各种网络请求,这些都有可能导致出现异常,这些异常在drf中是没有进行处理的,所以就会冒泡给django框架了,django框架会进行组织错误信息,作为html页面返回给客户端,所在在前后端分离项目中,可能js无法理解或者无法接收到这种数据,甚至导致js出现错误的情况。因此为了避免出现这种情况,我们可以自定义一个属于自己的异常处理函数,对于drf无法处理的异常,我们自己编写异常处理的代码逻辑。
针对于现有的drf的异常处理进行额外添加属于开发者自己的逻辑代码,一般我们编写的异常处理函数,会写一个公共的目录下或者主应用目录下。这里主应用下直接创建一个excepitions.py,代码:
from rest_framework.views import exception_handler from rest_framework.response import Response from rest_framework import status # 针对mysql、mongoDB、redis、第三方数据接口 def custom_exception_handler(exc, context): """ 自定义异常函数 exc: 异常实例对象,发生异常时实例化出来的 context: 字典,异常发生时python解释器收集的执行上下文信息。 所谓的执行上下文就是python解释器在执行代码时保存在内存中的变量、函数、类、对象、模块等一系列的信息组成的环境信息。 """ response = exception_handler(exc, context) if response is None: """当前异常,drf无法处理""" if isinstance(exc, ZeroDivisionError): response = Response({"detail":"数学老师还有30秒达到战场,0不能作为除数!"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) return response
在配置文件中声明自定义的异常处理,`settings`,代码:
REST_FRAMEWORK = { # 异常配置 'EXCEPTION_HANDLER': 'drfdemo.exceptions.custom_exception_handler' }
如果未声明自定义异常的话,drf会采用默认的方式,使用自己封装的异常处理函数,rest_frame/settings.py
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler' }
例如:
补充上处理关于数据库的异常,这里使用其他异常来举例:
`主应用.exceptions`,代码:
from rest_framework.views import exception_handler from rest_framework.response import Response from rest_framework import status from django.db import DatabaseError # 针对mysql、mongoDB、redis、第三方数据接口 def custom_exception_handler(exc, context): """ 自定义异常函数 exc: 异常实例对象,发生异常时实例化出来的 context: 字典,异常发生时python解释器收集的执行上下文信息。 所谓的执行上下文就是python解释器在执行代码时保存在内存中的变量、函数、类、对象、模块等一系列的信息组成的环境信息。 """ response = exception_handler(exc, context) print(f"context={context}") if response is None: """当前异常,drf无法处理""" view = context["view"] # 获取异常发生时的视图类 request = context["request"] # 获取异常发生时的客户端请求对象 if isinstance(exc, ZeroDivisionError): response = Response({"detail":"数学老师还有30秒达到战场,0不能作为除数!"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) if isinstance(exc, TypeError): print('[%s]: %s' % (view, exc)) response = Response({'detail': '服务器内部错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) return response
视图中,故意报错:
from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination class Hom5PageNumberPagination(PageNumberPagination): page_size = 10 # 默认分页的每一页数据量 max_page_size = 20 # 设置允许客户端通过地址栏参数调整的最大单页数据量 page_query_param = "pn" # 地址栏上代表页码的变量名,默认是 page page_size_query_param = "size" # 地址栏上代表单页数据量的变量名,默认是page_size class Hom5APIView(ModelViewSet): queryset = Student.objects # 这句话在没有进行分页时不会报错,调用了分页则会报错,所以会进入异常处理! # queryset = Student.objects.all() serializer_class = StudentModelSerializer # 局部分页 # pagination_class = None # 关闭分页功能 # 局部分页往往采用自定义分页类,进行分页数据量的配置 pagination_class = Hom5PageNumberPagination def list(self, request, *args, **kwargs): # 获取django的配置项 from django.conf import settings # 获取rest_framework的配置项 # from rest_framework.settings import api_settings # print(api_settings.DEFAULT_PAGINATION_CLASS) return super().list(request, *args, **kwargs)
### REST framework定义的异常
- APIException 所有异常的父类 - ParseError 解析错误 - AuthenticationFailed 认证失败 - NotAuthenticated 尚未认证 - PermissionDenied 权限拒绝 - NotFound 404 未找到 - MethodNotAllowed 请求方式不支持 - NotAcceptable 要获取的数据格式不支持 - Throttled 超过限流次数 - ValidationError 校验失败
也就是说,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。
# 8. 自动生成接口文档
REST framework可以自动帮助我们生成接口文档。
接口文档以网页的方式呈现。
自动接口文档能生成的是继承自`APIView`及其子类的视图
## coreapi
### 安装依赖
REST framewrok生成接口文档需要`coreapi`库的支持。
pip install coreapi
### 设置接口文档访问路径
在总路由中添加接口文档路径。 文档路由对应的视图配置为`rest_framework.documentation.include_docs_urls`, 参数`title`为接口文档网站的标题。总路由urls.py,代码: ```python from rest_framework.documentation import include_docs_urls urlpatterns = [ ... path('docs/', include_docs_urls(title='站点页面标题')) ] ```
在settings.py中配置接口文档。
INSTALLED_APPS = [ 'coreapi', ] REST_FRAMEWORK = { # 。。。 其他选项 # 接口文档 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', }
8.3. 文档描述说明的定义位置
1) 单一方法的视图,可直接使用类视图的文档字符串,如 ```python class BookListView(generics.ListAPIView): """ 返回所有图书信息 """ ``` 2)包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如 ```python class BookListCreateView(generics.ListCreateAPIView): """ get: 返回所有图书信息. post: 新建图书. """ ``` 3)对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分,如 ```python class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet): """ list: 返回图书列表数据 retrieve: 返回图书详情数据 latest: 返回最新的图书数据 read: 修改图书的阅读量 """ ```
### 访问接口文档网页
浏览器访问 127.0.0.1:8000/docs/,即可看到自动生成的接口文档。
两点说明:
1) 视图集ViewSet中的retrieve名称,在接口文档网站中叫做read
2)参数的Description需要在模型类或序列化器类的字段中以help_text选项定义,如:
```python class Student(models.Model): ... age = models.IntegerField(default=0, verbose_name='年龄', help_text='年龄') ... ``` 或 ```python class StudentSerializer(serializers.ModelSerializer): class Meta: model = Student fields = "__all__" extra_kwargs = { 'age': { 'required': True, 'help_text': '年龄' } } ```
## yasg
安装 ```bash pip install drf-yasg ``` 配置,settings.py,代码: ```python INSTALLED_APPS = [ 'drf_yasg', # 接口文档drf_yasg ] ``` 总路由, ```python from django.contrib import admin from django.urls import path, include from rest_framework.documentation import include_docs_urls # yasg的视图配置类,用于生成a'pi from drf_yasg.views import get_schema_view from drf_yasg import openapi schema_view = get_schema_view( openapi.Info( title="drf接口文档", # 必传 default_version='v1.0,0', # 必传 description="描述信息", terms_of_service='', contact=openapi.Contact(email="649641514@qq.com"), license=openapi.License(name="协议版本") ), public=True, # permission_classes=(rest_framework.permissions.AllowAny) # 权限类 ) urlpatterns = [ # 文档路由 path('doc/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger'), path('docs/', include_docs_urls(title='站点页面标题')), path('admin/', admin.site.urls), path('students/', include("students.urls")), path('sers/', include("sers.urls")), path('school/', include("school.urls")), path("req/", include("req.urls")), path("demo/", include("demo.urls")), path("opt/", include("opt.urls")), ] ```
http://127.0.0.1:8000/doc/,访问效果: