简介

  • 简言之是一种设计api的接口规范,是设计风格而不是标准,具象状态传输
  • 每个url代表一种资源,
    • json格式数据
    • text文本
    • 图片,视频等等
  • 客户端和服务器之间,传递这种资源的某种表现形式
    • 通过请求中的content-type来指明传给服务端的参数类型
      • text/plain,application/json
      • application/x-www-form-urlencoded
      • image/jpeg
      • text/html
    • 通过请求头中的Accept来指明希望接收服务端的数据类型。
  • 客户端通过HTTP动词指明对服务端资源进行的操作

    • crud

      • Create post方法, 201 返回码
      • Read get方法 200 返回码
      • Put update/replace 404 not found 200 or204 ok
      • Partial Update/Modify PATCH 404 200 or 204ok
      • Delete Delete 404 200 or 404

        REST 常用的设计规则

        1,URL

    • 命名

      • 尽量使用名词复数形式
      • 往往与数据库的表明对应
    • 差例示范
      • /getProjects 不应该带有动词
      • /listUsers
    • 优秀范例
      • /projects
      • /users
    • 过滤条件
      • 如果记录数量很多,服务器不可能将所有的数据都返回给前端
      • ?limit=10 指定返回记录的数量
      • ?offset=10 指定返回记录的开始位置
      • ?page=2&size=10 指定第几页和每页的数据条数
      • ?sort=name 指定返回结果按照哪个属性排序,以及排序顺序
    • 域名
      • 尽量使用专用域名
    • 版本
      • 在URL中呈现版本号
        • ex api.adi.site/app/0.1
      • 也可以在请求头中展现
        • accept : application/vnd.example.v0.2+json

          2,HTTP请求动词

  • 含义

    • 常见的http动词有下面几个(括号中为对应的sql命令
      • GET (SELECT 从服务器获取资源(一项或者多项
      • POST(CREATE 在服务器新建一个资源
      • PUT (UPDATE 在服务器更新资源(客户端提供改变后的完整资源
      • DELETE (DELETE 从服务器删除资源
    • 三个不常用的http动词
      • PATCH (UPDATE WHERE 在服务器部分更新资源,客户端提供改变的属性
      • HEAD 获取资源的元数据
      • OPTIONS 获取关于资源的哪些属性是客户端可以改变的信息
  • 例子

    • GET /projects 获取所有项目信息
    • POST /projects 创建一个新的项目
    • GET /projects/6 获取ID为6的项目信息
    • 多级的话在添加斜杠 /projects/6/interface

      3,状态码

  • 200 ok 服务器成功返回用户请求的数据

  • 201 created post/put/patch 用户新建或修改数据成功
  • 204 NO CONTENT delete 用户删除数据成功
  • 400 INVALID REQUEST POST/PUT/PATCH 用户请求有误(请求参数有误
  • 401 Unauthorized 表示用户没有权限 令牌,用户名,密码错误
  • 403 forbidden 表明用户得到授权 但是访问是禁止的
  • 404 not found 用户请求的路径不存在
  • 500 internal server error 服务器发生错误

    4,返回结果

  • GET /projects 返回所有项目的列表 json数组

  • GET projects/6 返回单个项目信息
  • POST projects 返回生成的项目信息 单个json
  • PUT projects/6 返回更新后,完整的项目信息(单个json
  • PATCH projects/6 返回更新之后,完整的项目信息(单个json
  • DELETE projects/6 返回空

    5,错误处理

    当请求有误时,服务器需将错误的信息以json格式数据的形式返回返回中包含状态码和错误信息

    6,Hypermedia API

    超链接api
    响应数据中,可以包含下一步操作的url连接

    传统写法与痛点

    创建接口的任务

  • 首先需要校验前端用户传来的数据—-将请求的数据(json)数据转换为模型类对象——-称为反序列化

    • 反序列化
      • 将用户在前端发来的数据格式转换成python可处理的数据类型
      • json,xml等格式的数据转换为python中课处理的类型
      • 将json格式的字符串转换为django中的模型类对象
    • 操作数据库
    • 序列化
      • 将模型类对象转化为要响应到前端页面上去的json数据格式
      • 例如将django的模型类对象转换为json字符串返回给前端。

        CRUD 一般化处理

  • 增 检验请求参数 - 反序列化 - 保存数据 - 序列化保存的对象并返回给前端页面

  • 删除 判断要修改的数据是否存在 - 执行数据库删除操作,返回状态码即可
  • 改 判断要修改的数据是否存在 - 检验请求参数 - 反序列化 - 保存数据 - 序列化返回
  • 查 查询数据库 - 将数据序列化返回

    思考上面的这种写法的痛点

    代码非常冗余,不符合优秀测开的风格,数据校验非常麻烦,复用性查。
    编码没有统一的规范,杂乱无章,不够简洁,
    总之问题非常多。不够优雅 ```python def post(self, request):

    1. # 新增项目 7.28
    2. # 从 前端获取json格式的数据,转化为python内部的数据格式
    3. # 为了严谨性,需要做各种复杂的校验,校验前端页面传来的数据
    4. # 比如 是否为json,传递的数据是否符合要求,必传参数是否携带
    5. json_data = request.body.decode('utf-8') # 请求体中取出json数据
    6. # 使用jsonloads转换成python数据
    7. python_data = json.loads(json_data, encoding='utf-8')
    8. # 向DB中新增项目
    9. # new_project = Projects.objects.create(name=python_data['name'],
    10. # leader=python_data['leader'],
    11. # tester=python_data['tester'],
    12. # programer=python_data['programer'],
    13. # publish_app=python_data['publish_app'],
    14. # desc=python_data['desc'])
    15. # 2020.7.30 反序列化将前端传来的数据序列化为python内部可处理的数据格式
    16. serializer = ProjectSerializer(data=python_data)
    17. # 校验前端输入的数据
    18. try:
    19. # 调用的时候才开始进行校验 如果校验成功,则返回True,校验失败返回F
    20. serializer.is_valid(raise_exception=True)
    21. # 拿到错误提示信息 # raise_exception 设置为True 验证不通过会抛出异常
    22. # 调用is_valid方法之后才能调用serializer.errors属性,如果验证通过,errors属性为空
    23. # 校验成功之后的数据可以使用vlaidated_data属性来获取
    24. # 是一个字典格式,
    25. except Exception as e:
    26. return JsonResponse(serializer.errors)

serializer.save()

    # 将创建成功的对象返回,将模型类对象转化为字典,然后返回
    # one_dict = {
    #         #     'name': project.name,
    #         #     "leader": project.leader,
    #         #     'programer': project.programer,
    #         #     'publish_app': project.publish_app,
    #         #     'desc': project.desc
    #         # }
    # 序列化输出返回给前端
    # project_serializer = ProjectSerializer(project)
    return JsonResponse(serializer.data, status=201)
里面的被注释掉的手写序列化过程就是要进行优化掉的部分 

<a name="USMdv"></a>
##  DRF
全称为django restframework

-  简介
   - 在django框架基础上进行的二次开发,用于构建restfulApi,简称为DRF 或者rest framework框架。
-  特性
   - 提供了serializer序列化器,可以高效的进行序列化反序列化操作,
   - 提供了极为丰富的类视图,mixin扩展类,viewset视图集
   - 提供了直观的 web api界面
   - 多种身份认证和权限认证
   - 强大的排序,过滤,分页,搜索,限流等功能
   - 可扩展性,插件丰富
- 安装
   - pip install djangorestframework
   - pip install  markdown
   - 在settings文件下进行app注册
   - rest_framework
<a name="1NJIN"></a>
## 序列化器 Serializer

- 数据校验
   - 判断用户输入的数据是否异常
- 数据转换
   - 反序列化
      - 数据格式(json,xml,text) =>程序中的数据类型
   - 序列化
      - 程序中的数据类型=> 数据格式(前端能处理的数据,如json)
- 使用
   - 定义序列化器
```python
class ProjectSerializer(serializers.Serializer):
    # 继承drf中的Serializer或者子类
    '''
    创建项目序列化器类
'''
    # label 选项,相当于verbose_name
    # help_text 与模型类中的字段作用一直
    # allow_blank 相当于模型类中的blank
    # allow_null 相当于模型类中的null
    # read_only = True 该字段只能进行序列化输出
    # 定义的序列化器字段,默认既可以序列化输出,也可以烦序列化输入
    # write_only = True 指定该字段只进行反序列化输入,但不进行序列化输出
    # 字段名与模型类中需要一致
    # 需要输出那些字段,就需要在序列化器中定义哪个字段
    # validators 属性中包含各种各样的验证器类
    name = serializers.CharField(label="项目名称",
                                 validators=[UniqueValidator(queryset=Projects.objects.all(),
                                                             message='指定的错误提示信息: ')],
                                 max_length=50, help_text="项目名称")
    leader = serializers.CharField(label="负责人",
                                   max_length=50, help_text="负责人")
    tester = serializers.CharField(label="测试人员",
                                   max_length=50, help_text="测试人员")
    programer = serializers.CharField(label="开发人员",
                                      max_length=50, help_text="开发人员")
    publish_app = serializers.CharField(label="发布应用",
                                        max_length=100, help_text="发布应用")
    desc = serializers.CharField(label="简要描述", allow_blank=True, default="", allow_null=True,
                                 max_length=100)
  • 自定义校验器方法的话,需要在序列化器类上访进行自定义,之后加入到validators列表中去 ```python

    如果要自定义校验器的话

    def is_unique_project_name(name):

    假设需求为,如果名字中没有包含项目关键字,不包含就不允许通过验证

    if ‘项目’ not in name: raise serializers.ValidationError(“项目名称中必须包含项目关键字”)

- 使用注意及字段说明
   - 注意点 序列化器定义的类属性往往需要与模型类字段一一对应,不对应的话就不能序列化输出,只可输入。
   - read_only write_only 不指定的话,既可以序列化输出(指的是输出到页面,反序列化输入(指的是输入到后台
- 对数据进行校验需要使用到序列化器的反序列化功能,就是从前端页面上获取到数据
   - 然后调用序列化器的方法,传入data参数,data理解成前端传来的数据,instance理解成要返回给前端的数据
      - 1,调用序列化器对象的is_valid方法,才开始进行校验
      - 2,如果校验成功,返回True,校验失败返回Fasle
      - 3,raise_exception=True 那么校验失败后,会抛出异常
      - 4,调用is_valid方法之后,才可以调用errors属性,获取检验的错误提示(字典类型)
   - # 校验成功之后的数据,可使用validated_data属性来进行获取,在反序列化的时候加入

如果校验没有通过,使用validated_data获取属性获取到的是空字典
<a name="vuEUF"></a>
## 序列化器 Serializer 反序列化
反序列化最重要的就是数据校验,包括字段类型校验、长度、是否唯一、是否只输入、是否只输出、是否允许为空等等,也可以做自定义的校验,总之校验功能及其强大
<a name="StOfy"></a>
### 1,序列化字段类型及参数
查看原码
```python
# This helps keep the separation between model fields, form fields, and
# serializer fields more explicit.
from rest_framework.fields import (  # NOQA # isort:skip
    BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField,
    DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField,
    HiddenField, HStoreField, IPAddressField, ImageField, IntegerField, JSONField,
    ListField, ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField,
    RegexField, SerializerMethodField, SlugField, TimeField, URLField, UUIDField,
)

字段及字段构造方式:
BooleanField:BooleanField()
CharField:CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
ChoiceField:ChoiceField(choices) choices与Django的用法相同
DateField:DateField(format=api_settings.DATE_FORMAT, input_formats=None)
DateTimeField:DateTimeField(format=api_settings.DATE_FORMAT, input_formats=None)
DecimalField:DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DictField:DictField(child=)
DurationField:DurationField()
EmailField:EmailField(max_length=None, min_length=None, allow_blank=False)
Field:Field(read_only=False, write_only=False,required=None, default=empty, initial=empty, source=None,label=None, help_text=None, style=None,error_messages=None, validators=None, allow_null=False)
FileField:FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
FilePathField:FilePathField(path, match=None, recursive=False, allow_files=True,allow_folders=False, required=None)
FloatField:FloatField(max_value=None, min_value=None)
HiddenField:HiddenField()
IPAddressField:IPAddressField(protocol=‘both’, unpack_ipv4=False, options)
ImageField:ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
IntegerField:IntegerField(max_value=None, min_value=None)
ListField:ListField(child=, min_length=None, max_length=None)
MultipleChoiceField:MultipleChoiceField(choices)
NullBooleanField:NullBooleanField()
RegexField:RegexField(regex, max_length=None, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
SlugField:SlugField(maxlength=50, min_length=None, allow_blank=False)
TimeField:TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
URLField:URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField**:UUIDField(format=‘hex_verbose’) format: 1) ‘hex_verbose’ 如”5ce0e9a5-5ffa-654b-cee0-1238041fb31a” 2) ‘hex’ 如 “5ce0e9a55ffa654bcee01238041fb31a” 3)‘int’ - 如: “123456789012312313134124512351145145114” 4)‘urn’ 如: “urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a”

选项参数

max_length:最大长度:在反序列化时进行输入最大长度校验
min_lenght:最小长度:在反序列化时进行输入最小长度校验
allow_blank:是否允许为空:在反序列化时允许传空白字符串,默认不允许
trim_whitespace:是否截断左右空白字符,默认True
max_value:最大值:在反序列化时进行输入最大值校验
min_value:最小值:在反序列化时进行输入最小值校验
通用参数:
read_only:表明该字段仅用于序列化输出,但在反序列化验证时不做校验,默认False
write_only:表明该字段仅用于反序列化输入,但在序列化时不进行输出,默认False
required:表明该字段在反序列化时必须输入,序列化时必须输出,默认True
default:反序列化时使用的默认值,如果不指明,在传递时默认值为0
allow_null:表明该字段是否允许传入null,默认False
validators:该字段使用的验证器
error_messages:包含错误编号与错误信息的字典
label:用于HTML展示API页面时,显示的字段名称,相当于模型类字段的verbose_name属性
help_text:用于HTML展示API页面时,显示的字段帮助提示信息

2,反序列化简单用法

创建序列化器对象

  • a.把前端传递的json格式参数转化为字典后,传递给data参数
  • b.调用序列化器对象.is_valid()方法,会开始进行校验,如果没有调用该方法则不校验
  • c.如果校验成功,返回True,否则返回False
  • d.必须调用is_valid()方法之后,返回了False才能调用序列化器对象.errors属性获取相应的报错信息,报错信息返回一个字典
  • e.必须调用is_valid()方法之后,返回了True才能调用序列化器对象.validated_data属性获取校验通过的信息,通过的信息返回一个字典
  • f.报错信息也可以通过给is_valid()方法将它的raise_exception属性设置为True,来主动抛出异常信息 ```python from django.http import JsonResponse from django.views import View from django.db import connection import json from .models import Projects from .serializers import ProjectSerializer

class ProjectsPage(View): ‘’’ 类视图 ‘’’ def post(self, request):

    input_data = json.loads(request.body)

    serializer_check_obj = ProjectSerializer(data=input_data)

    if not serializer_check_obj.is_valid():
        return JsonResponse({"code": 1, "res": "error", "msg": serializer_check_obj.errors})

    obj = Projects.objects.create(**input_data)

    serializer_obj = ProjectSerializer(instance=obj)

    return JsonResponse(serializer_obj.data, status=201)
在serializer序列化器中设置name字段最大为2
```python
name = serializers.CharField(max_length=2, label="项目名称", help_text='项目名称')

然后启动项目后使用postman调用该接口,验证是否进行了数据长度校验
DRF-RestfulApi & APIVIEW & Serializer - 图1
可以看到验证成功了,但是这种异常提示都是DRF框架自己设定的,那么如果自定义异常输出信息呢?
我们可以在序列化器类对应的字段类中,设置error_messages属性,该属性的值需要传一个字典,key设定为你需要自定义异常信息的校验项,值为自定义异常信息

name = serializers.CharField(max_length=2, label="项目名称", help_text='项目名称',
                                 error_messages={"max_length": "该字段长度不能大于2"})

我们再去调一下接口,返回如下:
DRF-RestfulApi & APIVIEW & Serializer - 图2

序列化器的重要属性

1.一定要先执行.is_valid()方法后才能访问.errors和.validated_data
2.可以不用调用.is_valid()方法就能访问.data

APIVIEW

思考一下
前面后端序列化给前端都是以json数据返回,反序列化post,put也以json数据格式输入如果我们反序列输入的数据野象以 application/x-www-form-urlencoded或者其他的格式输入的时候呢,又或者返回的数据想以html页面的方式展示要怎么做呢?

  • django的视图中,都需要继承View类,在使用drf框架之后,视图类可以继承APIVIEW类,
  • 与django中的view不同的是,drf中的apiview针对性的做了一系列拓展
  • 例如,认证,授权,限流和不同请求数据的解析等等

    APIVIEW继承于DJango中的view

    APIVIEW 与 VIEW对比

    传入视图方法中的事 Request对象,而不是Django的HttpRequest对象
    视图方法可以返回Respnse对象,会为响应数据处理(render)为前端要求的格式
    任何APIException异常都会被捕获到,并且处理成合适的响应信息
    在进行dispatch() 分发前,会对请求进行身份认证,权限检查,流量控制

    常用的类属性

    authentication_classes列表或元祖,身份认证类
    permission_classes 列表或者元祖,权限检查类
    throttle_classes 列表或元祖,流量控制类

    1,引入

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import status
    

    2,序列化Response<->Accept

    1.Response

    前面我们一般都使用的是JsonResponse来进行序列化输出,使用了APIView之后,我们可以使用Response序列化输出,它有以下特性:

  • 对Django中的HttpResponse进行了拓展

  • 会根据请求头中的Accept,自动转化响应数据到对应格式
    • 如果请求头中未设置Accept,则会采用默认方式处理响应数据(默认返回json格式的数据)
  • 可以指定响应默认渲染类
  • Response(data, status=None, template_name=None, headers=None, content_type=None)
  • 参数说明

    • data
      • 序列化处理后的数据
      • 一般为serializer.data(python基本数据类型:字典、嵌套字典的列表)
    • status
      • 状态码,默认为200
    • template_name
      • 模板名称,使用HTNLRenderer渲染时需指明
    • headers
      • 用于存放响应头信息的字典
    • content_type
      • 响应头中的Content-Type
      • 通常此参数无需设置,会自动根据前端所需类型数据来设置该参数


    部分代码展示: ```python from django.http import Http404 from django.db import connection from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import Projects from .serializers import ProjectsModelSerializer

class ProjectsPage(APIView): ‘’’ 类视图 ‘’’

def get_object(self, pk):
    try:
        obj = Projects.objects.get(id__exact=pk)
    except Exception:
        raise Http404("参数错误")
    return obj

def get(self, request, pk=None):
    if pk:
        obj = self.get_object(pk)
        serializer_obj = ProjectsModelSerializer(instance=obj)
        return Response({'code': 0, 'res': "success", 'msg': serializer_obj.data}, status=status.HTTP_200_OK)
    else:
        qs = Projects.objects.all()
        serializer_obj = ProjectsModelSerializer(instance=qs, many=True)
        return Response({'code': 0, 'res': "success", 'msg': serializer_obj.data}, status=status.HTTP_200_OK)
postman展示:<br />1,不指定Accept<br />![](https://cdn.nlark.com/yuque/0/2020/png/1608527/1596325598448-5f7375ba-648e-40f3-b7bc-36b2adb1328a.png#align=left&display=inline&height=558&margin=%5Bobject%20Object%5D&originHeight=558&originWidth=1560&size=0&status=done&style=none&width=1560)<br />1,进行Accept指定<br />![](https://cdn.nlark.com/yuque/0/2020/png/1608527/1596325627764-cce280c8-69ec-4017-b0da-eaa6c8ccf36f.png#align=left&display=inline&height=718&margin=%5Bobject%20Object%5D&originHeight=718&originWidth=1024&size=0&status=done&style=none&width=1024)
<a name="pMErt"></a>
##### 2,渲染类
我们不指定Accept的使用,默认为返回json数据,但这又是为什么呢?为什么不会返回别的类型数据呢?在drf的全局配置中,DEFAULTS属性值有一个渲染类的配置
```python
REST_FRAMEWORK = {
    # 默认响应渲染类配置
    'DEFAULT_RENDERER_CLASSES': (
        # JSON 渲染器为第一优先级
        'rest_framework.renderers.JSONRenderer',
        # 可浏览的api渲染器为第二优先级
        'rest_framework.renderers.BrowsableAPIRenderer'
    ),
    'DEFAULT_FILTER_BACKENDS': [
        'rest_framework.filters.OrderingFilter',
        'django_filters.rest_framework.backends.DjangoFilterBackend'
    ]
    ,
    # 设置分页器引擎
    # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'DEFAULT_PAGINATION_CLASS': 'utils.pagination.PageNumberModify',
    # 同时必须制定每页显示数据条数
    # 'PAGE_SIZE': 3
}

从该列表我们知道,不指定时为默认返回json,如果我们想返回除了JSONRenderer和BrowsableAPIRenderer之外的格式数据时,就需要将该列表在项目下的settings.py中的REST_FRAMEWORK属性中进行重写

3、反序列化Request<->Content-Type

1.Request

现在我们的需求不仅仅是只使用json传参,也需要支持x-www-form-urlencoded传参,在django中,获取json的参数使用的为request.body,获取x-www-form-urlencoded参数使用的为request.POST,获取查询字符串参数使用的为request.GET,那么针对post这样的请求,如果想同时支持json和x-www-form-urlencoded传参,有没有更好的方式呢?答案是有的,我们使用drf框架,视图继承APIView时,request就不是原来的HttpRequest对象了,而是drf中的Request对象了,我们打断点看一下

DRF-RestfulApi & APIVIEW & Serializer - 图3

当我们给post请求传入json或者x-www-form-urlencoded时,获取参数的方式都是request.data,查询字符串变成了request.query_params,因此我们可以轻轻松松地实现以上需求
针对Request,总结如下特性:

  • 对Django中的HttpRequest进行了拓展
    • 会根据请求头中的Content-Type,自动进行解析
    • 无论前端发送的哪种格式的数据,都可以以相同的方式读取
  • request.data
    • 类似于Django中的request.POST和request.FILES
    • 可以对POST、PUT、PATCH的请求体参数进行解析
    • 不仅支持form传参,也支持json格式传参
  • request.query_params
    • 类似于Django中的request.GET
    • 获取查询字符串参数
  • 支持Django HttpRequest中所有的对象和方法 ```python from django.http import Http404 from django.db import connection from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import Projects from .serializers import ProjectsModelSerializer

class ProjectsPage(APIView): ‘’’ 类视图 ‘’’

def post(self, request):

    serializer_check_obj = ProjectsModelSerializer(data=request.data)

    if not serializer_check_obj.is_valid():
        return Response({"code": 1, "res": "error", "msg": serializer_check_obj.errors},
                        status=status.HTTP_400_BAD_REQUEST)

    serializer_check_obj.save()

    return Response({'code': 0, 'res': "success", 'msg': serializer_check_obj.data},
                    status=status.HTTP_201_CREATED)

``` postman展示
1,json传参
DRF-RestfulApi & APIVIEW & Serializer - 图4
2,表单传参
DRF-RestfulApi & APIVIEW & Serializer - 图5

2,解析类

同样的,drf的配置中也有关于解析类的配置,如果需要拓展其它解析方式,依然可以重写
DRF-RestfulApi & APIVIEW & Serializer - 图6