一、Restful规范
RESTful是一种定义API接口的设计风格,AIP接口的编写规范,,尤其适用于前后端分离的应用模式中
这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源
我们可以使用任何一个框架都可以实现符合restful规范的API接口
10条规范
1 数据的安全保障,通常使用https协议进行传输
2 url地址中带接口标识:一般这样
| -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 |
| https://api.baidu.com/book |
| 注:一般提倡用资源的复数形式,在url链接中不要出现操作资源的动词,错误示范:https://api.baidu.com/delete-user |
| |
| |
| https://api.baidu.com/place/search |
| 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的响应状态码:https://blog.csdn.net/meng2lin/article/details/128955775 |
| -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接口开发,最核心最常见的一个过程就是序列化
序列化:
把我们识别的数据转换成指定的格式提供给别人。
例如:我们在django中获取到的数据默认是模型对象(queryset),但是模型对象数据无法直接提供给前端或别的平台使用,所以我们需要把数据进行序列化,变成字符串或者json数据,提供给别人。
反序列化:
把别人提供的数据转换/还原成我们需要的格式。
例如:前端js提供过来的json数据,对于python而言就是字符串,我们需要进行反序列化换成模型类对象,这样我们才能把数据保存到数据库中
ps:
序列化:drf称为 read 序列化
反序列化:drf称为 write 反序列化
三、作业:基于django原生编写5个接口
book表为例,写5个接口(后面你写的所有接口,都是这5个,及其变形)
-查询所有图书
-新增一本图书
-修改一本图书
-查询一本图书
-删除一本图书
models.py
| from django.db import models |
| |
| |
| |
| |
| class Book(models.Model): |
| name = models.CharField(max_length=32) |
| price = models.IntegerField() |
| publish = models.CharField(max_length=32) |
urls.py
| from app02 import views |
| urlpatterns = [ |
| path('admin/', admin.site.urls), |
| path('books/api/', views.BookView.as_view()), |
| path('books/api/<int:pk>/', views.BookDetailView.as_view()), |
| ] |
views.py
| from django.shortcuts import render,HttpResponse |
| |
| |
| |
| from django.http import JsonResponse |
| from .models import Book |
| from django.views import View |
| |
| import json |
| class BookView(View): |
| def get(self, request): |
| |
| books = Book.objects.all() |
| |
| |
| book_list = [] |
| for book in books: |
| book_list.append({'name':book.name, |
| 'price':book.price, |
| 'publish':book.publish}) |
| |
| |
| |
| return JsonResponse(book_list, safe=False, json_dumps_params={'ensure_ascii': False}) |
| |
| |
| def post(self, request): |
| |
| name = request.POST.get('name') |
| price = request.POST.get('price') |
| publish = request.POST.get('publish') |
| |
| book = Book.objects.create(name=name, price=price, publish=publish) |
| |
| return JsonResponse({'name': book.name, 'price': book.price, 'publish': book.publish}) |
| |
| class BookDetailView(View): |
| |
| def get(self, request, pk): |
| book = Book.objects.filter(pk=pk).first() |
| |
| return JsonResponse({'id': book.id, 'name': book.name, 'price': book.price, 'publish': book.publish}) |
| |
| def put(self, request, pk): |
| |
| book = Book.objects.filter(pk=pk).first() |
| |
| |
| |
| |
| |
| |
| |
| print(request.body) |
| book_dic = json.loads(request.body) |
| book.name = book_dic.get('name') |
| book.price = book_dic.get('price') |
| book.publish = book_dic.get('publish') |
| book.save() |
| |
| return JsonResponse({'id': book.id, 'name': book.name, 'price': book.price, 'publish': book.publish}) |
| |
| def delete(self, request, pk): |
| Book.objects.filter(pk=pk).delete() |
| return JsonResponse(data={}) |
3 drf介绍和快速使用
3.1 drf介绍
django 中有个app,djangorestframework:drf,帮助我们,快速实现符合resful规范的接口
下载:(有个坑)
| pip3.8 install djangorestframework==稍微将版本 |
如果你是django2, 直接这样装,装最新drf,他们不匹配---》pip会自动把django卸载,安装最新django,安装最新drf
django3 ,这样没有任何问题
pip3.8 install djangorestframework --upgrade

ps:补充一点点东西:
-如果写了一个包,或app,想给别人用---》把你写的包,放到pypi上别人pip install 安装---》使用
3.2 drf快速使用(不需要会)
urls.py
| from app01.views import BookView |
| from rest_framework.routers import SimpleRouter |
| router = SimpleRouter() |
| router.register('books', BookView, 'books') |
| urlpatterns = [ |
| ] |
| urlpatterns += router.urls |
views.py
| 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源码分析
4.1 基于APIView的5个接口
视图类
| from rest_framework.views import APIView |
| from .serializer import BookSerializer |
| from rest_framework.response import Response |
| |
| |
| class BookView(APIView): |
| |
| def get(self, request): |
| book_list = Book.objects.all() |
| |
| ser = BookSerializer(instance=book_list, many=True) |
| return Response({'code': 100, 'msg': '成功', 'result': ser.data}) |
| |
| def post(self, request): |
| ser = BookSerializer(data=request.data) |
| if ser.is_valid(): |
| ser.save() |
| return Response({'code': 100, 'msg': '成功'}) |
| |
| |
| class BookDetailView(APIView): |
| |
| def get(self, request, pk): |
| book = Book.objects.filter(pk=pk).first() |
| ser = BookSerializer(instance=book, many=False) |
| return Response({'code': 100, 'msg': '成功', 'result': ser.data}) |
| |
| |
| 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() |
| return Response({'code': 100, 'msg': '成功'}) |
| |
| def delete(self, request, pk): |
| Book.objects.filter(pk=pk).delete() |
| return Response({'code': 100, 'msg': '删除成功'}) |
| from rest_framework.views import APIView |
| from .serializer import BookSerializer |
| from rest_framework.response import Response |
| |
| |
| class BookView(APIView): |
| |
| def get(self, request): |
| book_list = Book.objects.all() |
| |
| ser = BookSerializer(instance=book_list, many=True) |
| return Response({'code': 100, 'msg': '成功', 'result': ser.data}) |
| |
| def post(self, request): |
| ser = BookSerializer(data=request.data) |
| if ser.is_valid(): |
| ser.save() |
| return Response({'code': 100, 'msg': '成功'}) |
| |
| |
| class BookDetailView(APIView): |
| |
| def get(self, request, pk): |
| book = Book.objects.filter(pk=pk).first() |
| ser = BookSerializer(instance=book, many=False) |
| return Response({'code': 100, 'msg': '成功', 'result': ser.data}) |
| |
| |
| 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() |
| return Response({'code': 100, 'msg': '成功'}) |
| |
| def delete(self, request, pk): |
| Book.objects.filter(pk=pk).delete() |
| return Response({'code': 100, 'msg': '删除成功'}) |
序列化类
| 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()), |
| ] |
4.2 CBV源码分析
| |
| 1 视图中写视图类,继承View,写跟请求方式同名的方法 |
| class BookView(View): |
| def get(self,request): |
| return 四件套 |
| 2 在路径用写 |
| path('books/', BookView.as_view()) |
| |
| |
| |
| |
| |
| |
| |
| @classmethod |
| def as_view(cls, **initkwargs): |
| def view(request, *args, **kwargs): |
| self = cls(**initkwargs) |
| res=self.dispatch(request, *args, **kwargs) |
| return res |
| return view |
| |
| |
| path('books/', view) |
| |
| |
| self.dispatch(request, *args, **kwargs) |
| |
| |
| |
| |
| |
| def dispatch(self, request, *args, **kwargs): |
| |
| if request.method.lower() in self.http_method_names: |
| |
| |
| handler = getattr(self, request.method.lower()) |
| else: |
| handler = self.http_method_not_allowed |
| |
| return handler(request, *args, **kwargs) |
| |
| |
| |
4.3 APIView执行流程分析
| |
| |
| -入口:path('books/', BookView.as_view())---》请求来了,执行BookView.as_view()(request) |
| -as_view 是谁的? APIView的as_view |
| @classmethod |
| def as_view(cls, **initkwargs): |
| |
| |
| view = super().as_view(**initkwargs) |
| view=csrf_exempt(view) |
| return view |
| |
| -path('books/', View类的as_view中的view,只是去掉了csrf的认证) |
| -请求来了,执行 【View类的as_view中的view,只是去掉了csrf的认证(request)】 |
| -执行:self.dispatch(request, *args, **kwargs), self要从根上找 |
| -self.dispatch 是APIView的dispatch,源码如下 |
| def dispatch(self, request, *args, **kwargs): |
| |
| request = self.initialize_request(request, *args, **kwargs) |
| self.request = request |
| try: |
| |
| self.initial(request, *args, **kwargs) |
| |
| |
| 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) |
作业
| |
| |
| |
| |
| |
| -写一个装饰器,装饰在fbv上,这个fbv可以接受前端的编码格式可以是urlencoded,form-data,json,取数据,都是从request.data中取 |
| |
| def load_json(func): |
| def inner(request, *args, **kwargs): |
| try: |
| request.data = json.loads(request.body) |
| except Exception as e: |
| request.data = request.POST |
| res = func(request, *args, **kwargs) |
| return res |
| |
| return inner |
| |
| |
| @load_json |
| def books(request): |
| |
| |
| print(request.data) |
| print(request.FILES) |
| return JsonResponse({'code': 100, 'msg': '成功'}) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步