测试平台开发 - REST 高阶

一、 自定义接口规范(渲染器)

1. 渲染器的基本原理

序列化在返回数据后并不是直接做为响应数据,而是经过渲染器的渲染,生成不同格式的响应内容 。

重构渲染器就是重写父类渲染器的render方法 。

1
render(self, data, accepted_media_type=None, renderer_context=None)
  • data : 响应数据(序列化器的.data属性),等同于renderer_context[“ response “].data的值
  • accepted_media_type=None : 可选的。如果提供,这是由内容协商阶段确定的所接受的媒体类型。
    • 根据客户端的 Accept: 头,这可能比渲染器的 media_type 属性更具体,可能包括媒体类型参数。例如 “application/json; nested=true” 。
  • renderer_context=None : 可选的。如果提供,这是一个由view提供的上下文信息的字典。
    • 默认情况下这个字典会包括以下键: view , request , response , args , kwargs 。
    • renderer_context[" view "] 对应调用的视图函数
    • renderer_context[" request "] 对应本次请求对象,包含所有请求数据,如请求头,请求参数等等
    • renderer_context[" response "] 对应本次响应对象,包含所有响应数据,如响应头,状态码,响应数据

2. REST 框架 渲染器配置

settings.py 中添加drf全局配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# settings.py

# rest框架配置
REST_FRAMEWORK = {
# 添加全局异常处理模块
'EXCEPTION_HANDLER': 'utils.exception.my_exception_handler',
# 默认的渲染器
'DEFAULT_RENDERER_CLASSES': (
# 默认的渲染器 配置
# 'rest_framework.renderers.JSONRenderer',
# 'rest_framework.renderers.BrowsableAPIRenderer',
'utils.renderers.MyRenderer',
)
}

utils.renderers.MyRenderer 对应的是你文件路径

创建 utils 目录,然后在下面新建 renderers.py 文件 和 exception.py 文件

3. 正常信息获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# renderers.py

# 通用返回过滤器
from rest_framework.renderers import JSONRenderer


# 继承空 返回 JSON 的渲染器
class MyRenderer(JSONRenderer):
# 重构 render 方法
def render(self, data, accepted_media_type=None, renderer_context=None):
# 默认把 data 作为响应数据
resp_content = data
if renderer_context:
status_code = renderer_context['response'].status_code # 响应状态码
if str(status_code).startswith('2'): # 以 2 开头表示 响应正常
# 判断响应内容是否为列表,如果不是则需要转换为列表形式(接口要求)
if not isinstance(resp_content, list):
resp_content = [resp_content]
res = {'msg': 'success', 'retcode': status_code, 'retlist': resp_content}
return super().render(res, accepted_media_type, renderer_context)
# 异常情况
return super().render(resp_content, accepted_media_type, renderer_context)

3. 异常信息获取

REST 默认情况可以处理的异常有

  • 在REST framework内部产生的 APIException 的子类异常。
  • 原生Django的 Http404 异常.
  • 原生Django的 PermissionDenied 异常.

异常处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# exception.py

# REST 框架异常处理器
from rest_framework.views import exception_handler


# 处理异常返回 过滤器
def my_exception_handler(exc, context):
# 获取标准的错误响应
response = exception_handler(exc, context)
if response:
# 成功捕获到异常
response.data['msg'] = 'error' # 错误信息标记
response.data['retcode'] = response.status_code # 同步状态码
response.data['error'] = str(exc) # 采用详细的议程消息
response.data.pop('detail') # 去除 detail 消息
return response

二、 在线接口文档 —- swagger

Django REST Swagger 项目已经不维护了,并且不支持最新的Django

drf-yasg项目作为接口文档生成器。yasg的功能非常强大,可以同时支持多种文档格式。

1. 配置安装

1. 安装最新版 drf-yasg

1
pip install -U drf-yasg

2. 注册

drf-yasg 属于django的插件,因此需要注册到配置文件中 settings.py

staticfiles : 默认的静态文件。默认是已经注册了的,如果没有注册需要手动注册

1
2
3
4
INSTALLED_APPS = [
'django.contrib.staticfiles',
'drf_yasg',
]

3. 配置路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
# 文档视图
from rest_framework import permissions
# 定义 swagger 视图
swagger_view = get_schema_view(
openapi.Info(
title='SQPT API',
default_version='v1',
description='SQPT接口文档',
terms_of_service='https://www.pupper.cn',
contact=openapi.Contact(email='pupper.cheng@gmail.com'),
license=openapi.License(name='BSD License'),
),
public=True, # 是否公开
# 权限 class 是元组类型
permission_classes=(permissions.AllowAny,),
)


urlpatterns = [
# 互动模式
path('swagger/', swagger_view.with_ui('swagger', cache_timeout=0,)),
# 文档模式
path('redoc/', swagger_view.with_ui('redoc', cache_timeout=0,)),
]

2. 定制化用法(viewset 模式)

函数视图 : 采用swagger_auto_schema装饰器 修饰视图函数

1
2
3
4
5
6
7
8
# views.py

from drf_yasg.utils import swagger_auto_schema

@swagger_auto_schema(method='GET',operation_summary='定制化API',operation_description='接口描述。。')
@api_view(['GET'])
def customer_api(request):
return Response(data={"retcode":status.HTTP_200_OK,'msg':'building...'})

类视图 : 采用 django 装饰器 配合 swagger 的 装饰器 来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
# views.py

from django.utils.decorators import method_decorator
from drf_yasg.utils import swagger_auto_schema

@method_decorator(name='list', decorator=swagger_auto_schema(operation_description="列出所有步骤数据"))
@method_decorator(name='create', decorator=swagger_auto_schema(operation_description="创建步骤"))
@method_decorator(name='update', decorator=swagger_auto_schema(operation_description="更新步骤"))
@method_decorator(name='destroy', decorator=swagger_auto_schema(operation_description="删除步骤"))
@method_decorator(name='retrieve', decorator=swagger_auto_schema(operation_description="提取单个步骤数据"))
class CaseViewSet(viewsets.ModelViewSet):
queryset = Case.objects.all()
serializer_class = CaseSerializer

三、 前端对接

前端样式地址 提取码:sqpt

配置 Django 静态文件服务, 是 开发时 使用的 一种 临时方案

autotpsite/urls.py 文件 中配置静态服务

1
2
3
4
5
6
7
8
9
10
# autotpsite/urls.py

# 静态文件服务
from django.conf.urls.static import static

urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('sqpt.urls')),

]+static('/', document_root='dist') # 配置静态文件访问路径

如果 http请求的url 不是以 api/project 开头, Django 就会认为是要访问 dist目录下面的静态文件。

当我们访问html页面时,js代码会自动请求后台数据,渲染到当前页面上,为了适配前端,减少改动工作,先将后台URL进行改动

1
2
3
4
5
# autotpsite/urls.py

urlpatterns = [
path('api/', include('sqpt.urls')),
]

四、 接口开发完善

1. 嵌套字段内容显示

1
2
{"msg":"success","retcode":200,"retlist":
[{"id":1,"file_path":"haiwen_Test.yml","config":1,"suite":null}]}

显示 config 的具体内容:指定 config 为对应的序列化器对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# serializers.py

class ConfigSerializer(serializers.ModelSerializer):
class Meta:
model = Config
fields = '__all__'


class CaseSerializer(serializers.ModelSerializer):
# config 字段为 Config 序列化器 , REST 会自动提取其值
config = ConfigSerializer()

class Meta:
model = Case
fields = '__all__'

如果 ConfigSerializerCaseSerializer 的 下方,那么 config = ConfigSerializer() 会报错

已经展示了config 嵌套字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"msg": "success",
"retcode": 200,
"retlist": [
{
"id": 1,
"config": {
"id": 1,
"name": "用例1",
"base_url": "http://localhost",
"variables": null,
"parameters": null,
"verify": false,
"export": null
},
"file_path": "haiwen_Test.yml",
"suite": null
}
]
}

2. displayname

获取 method 具体的值:

1
2
3
4
5
6
7
8
9
10
11
12
# serializers.py

# 请求模型的序列化器
class RequestSerializer(serializers.ModelSerializer):
method = serializers.SerializerMethodField() # 自定义字段序列化返回方法

def get_method(self, obj): # rest 框架获取 method 时,自动调用该方法
return obj.get_method_display() # 返回 choice 的 displayname 而不是实际值

class Meta:
model = Request # 指定对应的模型
fields = '__all__' # 显示对应模型的所有字段

重新访问接口,method字段返回了可读内容