美文网首页
11、Django_RESTful_了解、传统例子及其痛点

11、Django_RESTful_了解、传统例子及其痛点

作者: 猪儿打滚 | 来源:发表于2019-11-13 15:14 被阅读0次

REST

  • REST是REpresentationsal State Transfer的缩写,具象状态转换
  • RESTful是一种开发理念,是一种API设计风格,而不是标准

主要的规范

  • 每一个URL代表一种资源,具体要对资源进行什么操作,是根据请求方式来区分的,而不是在URL上直接体现:GET http://www.xxx.com/get_user/01(不安全、繁琐、难维护等弊端),可以设计成GET http://www.xxx.com/user/01
  • 后端返回的资源数据类型有:
    1.json格式数据
    2.text文本
    3.图片、视频等
  • 客户端和服务器之间,传递这种资源的某种表现形式
    1.后端通过请求头Content-Type来指明传给服务端的参数类型,比如application/json
    2.客户端通过请求头中Accept来指明希望后端返回的相应数据的类型,比如application/json
  • 客户端通过请求方式的不同,指明对服务器资源进行不同的操作
    1.GET:read
    2.POST:create
    3.PUT:update/replace
    4.PATCH:partial/update/modify
    5.DELETE:delete

REST常用的设计规范

一、URL

1.命名

  • 尽量使用名词复数形式
  • 往往与数据库的表名对应

2.过滤条件

  • 如果记录数量很多,则需添加条件进行限制,否则返回数据太多,影响性能
?limit = 10 # 指定返回记录的数量
?offset = 10 # 指定返回记录的开始位置
?currPage=2&pageSize=10 # 指定第几页和每页多少条数据
?sort=name # 指定返回结果按照指定的属性进行排序

3.域名

  • 尽量使用专用域名
http

4.版本号

  • 在url中呈现版本号
http://xxx/app/1.0/
http://xxx/app/1.1/
  • 也可以在请求头呈现
Accept:application/vnd.example.v1.0+json
Accept:application/vnd.example.v1.1+json;version=1.1
二、请求方式/动词
  • 常见的http请求(括号中对应的sql命令)
    GET(select):从服务器获取资源(一个或多个)
    POST(create):在服务器创建一个资源
    PUT(update):在服务器更新资源(客户端提供所要更新的完整资源,比如要更新user的age,但如果是put,需要提供user的全部数据)
    DELETE(delete):从服务器删除资源
  • 下面是不常见的三种
    PATCH(update where):在服务器进行部分资源的更新(客户端提供改变的属性)
    HEAD:获取资源的元数据
    OPTIONS:获取关于资源的哪些属性是客户端可以改变的信息,一般后面会跟其它常用请求;比如特殊的post请求前会先发出options请求
  • 例子
GET /projects # 获取所有项目信息
POST /projects # 创建一个新项目 
GET / projects/6 # 获取id=6的项目信息 
PUT /projects/6 # 更新id=6的项目信息(全更新)
PATCH /projects/6  # 更新id=6的项目信息(部分更新)
DELETE /projects/6 # 删除id=6的项目
''' 多层级信息的获取的设计 '''
GET /projects/6/interfaces # 获取id=6的项目信息中所有的接口信息
GET /projects/6/interfaces/1 #获取id=6的项目信息中id=1的接口信息
3、常见状态码(RESTful)
200 ok - [GET]:服务器成功返回用户请求的数据
201 CREATED - [POST/PUT/PATCH]:用户创建成功或修改数据成功
204 NO CONTENT - [DELETE]:用户删除数据成功
400 INVALID REQUEST - [POST/PUT/PATCH]:用户请求有误(请求参数有问题)
401 Unauthorized = [^] :表示用户没有权限(cookies、用户名、密码错误)
403 Forbidden - [^]:表示用户得到授权(和401错误相对),但是访问是禁止的
404 NOT FOUND - [^]:表示用户请求的路径不存在
500 INTERNAL SERVER ERROR - [^]:服务器发生错误

4、返回结果要求(RESTful)

  • 服务器的响应数据应该符合以下规范
GET /projects # 返回所有项目信息的列表(json数组)
GET / projects/6 #返回一个项目的列表 (一个json)
POST /projects # 返回新生成的项目的列表 (一个json)
PUT /projects/6 # 返回更新之后,完整的项目信息(一个json)
PATCH /projects/6  # 返回更新之后,完整的项目信息(一个json)
DELETE /projects/6 # 删除id=6的项目 # 返回空

5、错误处理

  • 当请求有误时,服务器需将错误信息以json的格式返回,比如
{
    "code":1001,
    "data":null,
    "msg":"用户不存在"
}

6、超链接API

  • 响应数据中,可以包含下一步操作的url链接(比如说上一页、下一页的链接字段)

传统编写CRUD

1.已有代码
  • 已经创建好的model
  • login/models.py
from django.db import models


class User(models.Model):
    gender = (
        ('male', '男'),
        ('female', '女')
    )

    name = models.CharField(max_length=128, unique=True)  # 昵称唯一
    password = models.CharField(max_length=256)
    email = models.EmailField(unique=True)
    sex = models.CharField(max_length=32, choices=gender, default='女')
    c_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['-c_time']
        verbose_name = '用户'
        verbose_name_plural = '用户'
  • Django_Project/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('login.urls'))
]
2.创建对项目数据进行CRUD操作的接口
  • login/views.py
from login.serializer import UserModelSerializer
from rest_framework.viewsets import ModelViewSet
from django.http import JsonResponse
from login.models import User
from django.views import View
import json


class Users(View):

    def get(self, request):  # get请求中,就算用不上request,也需要设置这个参数
        """获取所有的User信息,返回类型为json"""
        # 1.数据库中获取所有的项目信息
        user_qs = User.objects.all()
        user_list = []
        # 2.遍历返回的查询集,同一个查询集的内容组装成json,并且所有的json都存放在一个列表中
        for user in user_qs:
            user_list.append(
                # 序列化:把变量从内存中变成可存储或传输的过程(比如dump/dumps、把数据转成json/把数据写入内存)
                {
                    'name': user.name,
                    'password': user.password,
                    'email': user.email,
                    'sex': user.sex,
                    'c_time': user.c_time
                }
            )
        # JsonResponse()第一个参数需要是dict,否则需要指定safe=False,不然报错
        return JsonResponse(user_list, safe=False, status=200)

    def post(self, request):
        """ 新创建User """
        # 1.获取请求中body的值,并解码
        user = request.body.decode('utf8')  # 注意body是属性,不是方法,不需要()
        # 2.还需要判断是否是json,并且必参是否填写(此处略过,痛点之一)
        # 3.把json数据反序列化成python类型的dict
        user_dict = json.loads(user, encoding='utf8')
        # 4.创建新的User到数据库中
        user = User.objects.create(**user_dict)
        # 5.新生成的user转成json(也称为序列化)
        user_new = {
            'name': user.name,
            'password': user.password,
            'email': user.email,
            'sex': user.sex,
            'c_time': user.c_time
        }

        # 6.响应返回
        return JsonResponse(user_new, status=201)


class UserDetail(View):

    def get(self, request, pk):
        """ 根据id=pk查询(pk是django默认创建的主键,和id一样) """

        # 1.此处用get对id进行查询,因为主键唯一,只会返回一个值
        # 此处应该对pk进行int判断,略过,痛点之一
        user = User.objects.get(id=pk)
        # 2.序列化,转成json
        user_dict = {
            'name': user.name,
            'password': user.password,
            'email': user.email,
            'sex': user.sex,
            'c_time': user.c_time
        }
        # 3. 响应
        return JsonResponse(user_dict, status=200)

    def put(self, request, pk):
        """ 根据id查询user,并更新改user的全部信息;请求中需有id和user更新后的全部信息 """
        # 1.此处用get对id进行查询,因为主键唯一,只会返回一个值
        # 此处应该对pk进行int判断,略过,痛点之一
        old_user = User.objects.get(id=pk)
        # 2.获取请求中修改后的user的信息,并反序列化成python的dict
        user_dict = request.body.decode('utf8')
        # 3.更新为新的user信息(因为是全部字段都更新,所以不使用update)
        # 3.1.反序列化
        user_value = json.loads(user_dict, encoding='utf8')
        # 3.2.替换值
        old_user.name = user_value['name']
        old_user.password = user_value['password']
        old_user.email = user_value['email']
        old_user.sex = user_value['sex']
        old_user.c_time = user_value['c_time']
        # 3.3.保存
        old_user.save()
        # 4.新user序列化
        user_data = {
            'name': old_user.name,
            'password': old_user.password,
            'email': old_user.email,
            'sex': old_user.sex,
            'c_time': old_user.c_time
        }
        # 5.响应
        return JsonResponse(user_data, status=201)

    def delete(self, request, pk):
        """根据id=pk进行删除操作  ORM的删除是先查询再删除"""
        # 1.查询
        # 此处应该对pk进行int判断,略过,痛点之一
        user = User.objects.get(id=pk)
        # 2.删除
        user.delete()
        # 3.响应{}
        return JsonResponse({}, safe=False, status=204)
  • login.urls.py
from django.urls import path
from login import views

app_name = 'login'

urlpatterns = [
    path(r'users/', views.Users.as_view()),
    path(r'users/<int:pk>/', views.UserDetail.as_view()),
]

设计总结

1.序列化
  • 首先思考下:内存中的字典、列表、集合以及各种对象,如何保存到一个文件中?
    1.设计一套协议,按照某种规则,把内存中的数据保存到文件中,文件是一个个字节序列。所以必须把数据额转换为字节序列,输出到文件,这就是序列化,反之,从文件的字节 序列恢复到内存中,就是反序列化。
    2.反序列化则相反
  • 序列化在Json中:
    1.json().dump:把数据转成json格式字符串并写入文件
    2.json().dumps:读取文件数据,并转成json格式字符串
2.增删改查的步骤

  • 校验请求参数 -> 反序列化查询到的数据 -> 保存数据 -> 将保存的对象序列化并返回

  • 判断要删除的数据是否存在 -> 执行数据删除

  • 判断要修改的数据是否存在 -> 校验请求参数 -> 反序列化查询到的数据 -> 保存数据 -> 将保存的数据序列号并返回

  • 查询数据库 -> 将查询到的数据序列化并返回

痛点

  • 代码冗余极其严重,不符合优开发风格(代码能少写就少写)
  • 数据校验非常麻烦,且可复用性差
  • 编码没有统一的规范,杂乱无章的感觉
  • 仅支持json格式的传参,不支持form表单传参
  • 仅能返回json格式的数据,其他类型不支持
  • 列表页视图没有分页,过滤,排序功能

相关文章

网友评论

      本文标题:11、Django_RESTful_了解、传统例子及其痛点

      本文链接:https://www.haomeiwen.com/subject/iwuabctx.html