Restful规范和drf

发布时间 2023-05-16 21:15:51作者: 橘子熊何妨

Restful规范和drf

Restful规范

# RESTful是一种定义API接口的设计风格,AIP接口的编写规范,,尤其适用于前后端分离的应用模式中
# 任何框架都可以实现符合restful规范的API接口
# 10条规范
"""
	1. 为了数据安全保障一般使用https协议传输,https = http+ssl
	2. URL地址中带API
		https://api.baidu.com
        https://www.baidu.com/api
    3. 多版本共存url中带版本号
    	https://api.baidu.com/v1/login/
		https://api.baidu.com/v2/login/
	4. URL地址尽量写名词,数据就是资源
		https://api.baidu.com/users
		https://api.baidu.com/books
		URL中提倡使用复词
		有些特殊情况不得不用动词
		https://api.baidu.com/login
	5. 资源操作由请求方式决定
        https://api.baidu.com/books   - get请求:获取所有书
        https://api.baidu.com/books/1 - get请求:获取主键为1的书
        https://api.baidu.com/books   - post请求:新增一本书书
        https://api.baidu.com/books/1 - put请求:整体修改主键为1的书
        https://api.baidu.com/books/1 - delete请求:删除主键为1的书
    6. url地址中?后带过滤条件
    	https://api.baidu.com/books -get请求表示查询所有图书,要查名字中有红的图书
    	https://api.baidu.com/books?name_contains=红
    	https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
        https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
        https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
        https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
        https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
	7. 响应状态码(http响应中带状态码)
		HTTP的响应状态码
            1xx:请求正在处理
            2xx:请求成功 200  201
            3xx:重定向
            4xx:客户端错误
            5xx:服务的错误
		http的响应的数据中带状态码(公司自己规定的)
    		{'code':100}
    8. 返回的数据中带错误信息
		{'code':101,'msg':'用户名或密码错误'}
    	{'code':100,'msg':'成功'}
    9. 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范(基本上都是自己的规范,很少完全遵守)
    	GET  /books:返回资源对象的列表(数组)
    		-[{name:三国演义,price:88},{name:西游记,price:88}]
        	-{code:100,msg:成功,data:[{name:三国演义,price:88},{name:西游记,price:88}]}
        GET /books/1:返回单个资源对象
        	-{name:三国演义,price:88}    ---{code:100,msg:成功,data:{name:三国演义,price:88}}
        POST /books:返回新生成的资源对象
        	-{id:4,name:三国演义,price:88}  ---{code:100,msg:成功}
        PUT /books/4:返回完整的资源对象
        	-{id:4,name:三国演义,price:188}  ---{code:100,msg:修改成功}
        DELETE /books/4: 返回一个空文档      ---{code:100,msg:删除成功}
	10. 返回的结果中带url链接
"""

序列化和反序列化

# api接口开发,最核心的就是序列化和反序列化
# 序列化就是把python可以识别的数据转化为指定的格式,如字符串或json数据供别人使用
# 反序列化就是把字符串或者json的数据转化为python可以识别的数据

# 序列化:drf称为 read      序列化
# 反序列化:drf称为 write   反序列化

drf介绍和快速使用

# book表为例,写5个接口(后面你写的所有接口,都是这5个,及其变形)
	-查询所有图书
    -新增一本图书
    -修改一本图书
    -查询一本图书
    -删除一本图书

drf介绍

# django中下载djangorestframework这个app,使用drf可以快速实现符合resful规范的接口

# 注意:Django3以下的版本直接装drf可能直接被升级到最新版,所以推荐先升级到Django3.x

# 补充一点点东西:
	-如果写了一个包,或app,想给别人用---》把你写的包,放到pypi上别人pip install 安装---》使用

drf快速使用(不需要会)

路由

from app01.views import BookView
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('books', BookView, 'books')
urlpatterns = [
]
urlpatterns += router.urls

视图

from .serializer import BookSerializer
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

序列化类 serializer.py

from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

drf之APIView源码分析

基于APIView的5个接口

视图类

from rest_framework.views import APIView  # APIView继承了djagno原来的View
from .serializer import BookSerializer
from rest_framework.response import Response


class BooksView(APIView):
    back_dict = {'code': 200, 'msg': '成功', 'data': {}}

    def get(self, request):
        book = Book.objects.all()
        ser = BookSerializer(instance=book, many=True) # 序列化
        self.back_dict['msg'] = '查看成功'
        self.back_dict['data'] = ser.data
        return Response(self.back_dict)

    def post(self, request):
        ser = BookSerializer(data=request.data) # 反序列化
        if ser.is_valid():  # 参数验证
            ser.save()  # 保存数据到数据库
        self.back_dict['msg'] = '添加成功'
        self.back_dict['data'] = ser.data
        return Response(self.back_dict)


class BookDetailView(APIView):
    back_dict = {'code': 200, 'msg': '成功', 'data': {}}

    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book, many=False) # 序列化
        self.back_dict['data'] = ser.data
        return Response(self.back_dict)

    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book, data=request.data) # 反序列化
        if ser.is_valid():  # 参数验证
            ser.save()  # 保存数据到数据库
        self.back_dict['msg'] = '修改成功'
        self.back_dict['data'] = ser.data
        return Response(self.back_dict)

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        self.back_dict['msg'] = '删除成功'
        del self.back_dict['data']
        return Response(self.back_dict)

序列化类

from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

路由

urlpatterns = [
    path('books/', BookView.as_view()),
    path('books/<int:pk>/', BookDetailView.as_view()),
]

CBV源码分析

# cbv写法:
	1 视图中写视图类,继承View,写跟请求方式同名的方法
    	class BookView(View):
            def get(self,request):
                return 四件套
     2 在路径用写
    	path('books/', BookView.as_view())
        
        
        
# 如上写法,为什么能够执行

# 前置条件:前端请求,一旦路径匹配成功,就会执行  BookView.as_view()(request传入,)
# 入口在  BookView.as_view()--->执行结果---》View中有个as_view类的绑定方法
    @classmethod
    def as_view(cls, **initkwargs):  # cls就是BookView这个类
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)  # 现在self就是BookView的对象了
            res=self.dispatch(request, *args, **kwargs)
            return res
        return view
    
# 执行结果是view 的内存地址: 请求来了,执行view(request)
	path('books/', view)
    
# 执行 View类中的as_view方法中的内层的view函数,路由匹配成功,本质是在执行
	self.dispatch(request, *args, **kwargs)
    # self是谁的对象?BookView的对象
    # 去BookView中dispatch,找不到,去父类,View中找到了
    
    
# View这个类的dispatch
    def dispatch(self, request, *args, **kwargs):
        # request.method.lower() 如果是get请求,  ‘get’ 在这个列表里面
        if request.method.lower() in self.http_method_names:
            # handler=getattr(BookView的对象,'get')   
            # handler就是BookView类中的get方法
            handler = getattr(self, request.method.lower())  # 反射就是验证第一个参数的那个对象里面是否有名字叫第二个参数提交的字符串的那个方法,如果有则返回一个方法,没有则将第三个参数返回
        else:
            handler = self.http_method_not_allowed
        # 执行 BookView类中的get方法 (request)
        return handler(request, *args, **kwargs)
    
# 最终本质跟写fbv的执行流程一样
# 最终结论:什么请求方式,就会执行视图类中的什么方法

APIView执行流程分析

# 有了drf,后期都写CBV,都是继承APIView及其子类
# 执行流程:
	-入口:path('books/', BookView.as_view())---》请求来了,执行BookView.as_view()(request)
    -as_view 是谁的? APIView的as_view
        @classmethod
        def as_view(cls, **initkwargs):
            # super()代指的是:父类对象  View类的对象
            # View的as_view(**initkwargs)----》执行结果是view,是View类的as_view方法中的view
            view = super().as_view(**initkwargs)
            view=csrf_exempt(view)  # 局部禁用csrf,
            return view
        
  	-path('books/', View类的as_view中的view,只是去掉了csrf的认证)
	-请求来了,执行 【View类的as_view中的view,只是去掉了csrf的认证(request)】
    -执行:self.dispatch(request, *args, **kwargs),self要从根上找,此时这个self是BookView的对象,所以还是得往父类APIView找
	-self.dispatch 是APIView的dispatch,源码如下
        def dispatch(self, request, *args, **kwargs):
            # request 是新的request,      request是老的request
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            try:
                # 执行了认证,权限和频率
                self.initial(request, *args, **kwargs)
                # 在执行视图类方法之前,去掉了csrf认证,包装了新的request,执行了认证频率和权限
                #### 执行请求方式字符串对应的方法
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
                response = handler(request, *args, **kwargs)
            except Exception as exc:
                response = self.handle_exception(exc)
                
            # 无论是在三大认证,还是视图类的方法中,出现错误,都会被异常捕获,统一处理
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
    

    
# 总结:
	1  以后只要继承APIView的所有视图类的方法,都没有csrf的校验了
    2  以后只要继承APIView的所有视图类的方法 中的request是新的request了
    3  在执行视图类的方法之前,执行了三大认证(认证,权限,频率)
    4  期间除了各种错误,都会被异常捕获,统一处理
        
        
        
# 补充(装饰器语法糖):
	fbv,局部禁用csrf,如何写?
    @csrf_exempt
    def index(request):
        pass
    
    本质原理是(装饰器本质):index=csrf_exempt(index)

books的五个接口(cbv)

视图类

import json

from django.http import JsonResponse
from django.views import View

from .models import Book


# Create your views here.


class BooksView(View):
    back_dict = {'code': 200, 'msg': '成功', 'data': {}}

    def get(self, request):
        book = Book.objects.all()
        book_lis = []
        for i in book:
            book_lis.append({'title': i.title, 'price': i.price})
        self.back_dict['msg'] = '查看成功'
        self.back_dict['data'] = book_lis
        return JsonResponse(self.back_dict)

    def post(self, request):
        title = request.POST.get('title')
        price = request.POST.get('price')
        Book.objects.create(title=title, price=price)
        self.back_dict['msg'] = '添加成功'
        self.back_dict['data'] = {'title': title, 'price': price}
        return JsonResponse(self.back_dict)


class BookDetailView(View):
    back_dict = {'code': 200, 'msg': '成功', 'data': {}}

    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        self.back_dict['data'] = {'title': book.title, 'price': book.price}
        return JsonResponse(self.back_dict)

    def put(self, request, pk):
        res = json.loads(request.body)
        title = res.get('title')
        price = res.get('price')
        Book.objects.filter(pk=pk).update(title=title, price=price)
        self.back_dict['msg'] = '修改成功'
        self.back_dict['data'] = res
        return JsonResponse(self.back_dict)

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        self.back_dict['msg'] = '删除成功'
        del self.back_dict['data']
        return JsonResponse(self.back_dict)

路由

urlpatterns = [
    #    path('admin/', admin.site.urls),
    path('books/', BooksView.as_view()),
    path('books/<int:pk>', BookDetailView.as_view()),
]

fbv和cbv加装饰器

fbv

@auth
def index(request):
    print(request.data)
    return JsonResponse({'name': '张三'})

装饰器

def auth(func):
    def inner(request, *args, **kwargs):
        request_format = request.headers.get('Content-Type')
        if 'urlencoded' in request_format:
            request.data = request.POST
        elif 'json' in request_format:
            request.data = json.loads(request.body)
        elif 'form-data' in request_format:
            request.data = request.body
        else:
            print('未知参数')
        return func(request, *args, **kwargs)

    return inner

cbv

@method_decorator(auth, name='get')
class BooksView(View):
    back_dict = {'code': 200, 'msg': '成功'}

    def get(self, request):
        book = Book.objects.all()
        book_lis = []
        for i in book:
            book_lis.append({'title': i.title, 'price': i.price})
        self.back_dict['msg'] = '查看成功'
        self.back_dict['data'] = book_lis
        return JsonResponse(self.back_dict)

    @method_decorator(auth)
    def post(self, request):
        # title = request.POST.get('title')
        # price = request.POST.get('price')
        title = request.data.get('title')
        price = request.data.get('price')
        Book.objects.create(title=title, price=price)
        self.back_dict['msg'] = '添加成功'
        self.back_dict['data'] = {'title': title, 'price': price}
        return JsonResponse(self.back_dict)


class BookDetailView(View):
    back_dict = {'code': 200, 'msg': '成功'}

    @method_decorator(auth)
    def dispatch(self, request, *args, **kwargs):
        return super(BookDetailView, self).dispatch(request, *args, **kwargs)

    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        self.back_dict['data'] = {'title': book.title, 'price': book.price}
        return JsonResponse(self.back_dict)

    def put(self, request, pk):
        res = request.data
        title = res.get('title')
        price = res.get('price')
        Book.objects.filter(pk=pk).update(title=title, price=price)
        self.back_dict['msg'] = '修改成功'
        self.back_dict['data'] = res
        return JsonResponse(self.back_dict)

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        self.back_dict['msg'] = '删除成功'
        return JsonResponse(self.back_dict)