一.HTTP协议与wsgi协议
http协议 应用层协议 请求与响应规范:首行 - 头 - 体 特点:无状态、无连接、请求永远是客户端到服务器端、ssl wsgi协议 原生django启动项目 - 启动了server socket - wsgiref - uWSGI(项目上线) 规定数据的解析方式: get数据、post数据(数据数据) => request => 回调的视图函数 返回响应对象 - HTTPResponse类对象 - 数据、响应状态码
二.接口
url链接:http://api.oldboy.com/login/ 请求方式:get | post | put ... 请求参数:username | password 响应结果:result | data
利用Postman连接百度接口:
method: GET url: https://api.map.baidu.com/place/v2/search params: ak: 6E823f587c95f0148c19993539b99295 region: 上海 query: 肯德基 output: json
原生Djiango实现接口:
models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2)
class Meta:
db_table = 'old_boy_book'
verbose_name = '书籍'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class User(models.Model): SEX_CHOICES = [ (0, '男'), (1, '女'), (2, '哇塞') ] username = models.CharField(max_length=64) password = models.CharField(max_length=64) sex = models.IntegerField(choices=SEX_CHOICES, default=0) # 需要配置media工作目录与路由 icon = models.ImageField(upload_to='icon', default='/icon/default.png') class Meta: db_table = 'old_boy_user' verbose_name = '用户' verbose_name_plural = verbose_name def __str__(self): return self.username
创建Django项目并设置默认app名为api,完成路由分发: # 主路由 from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/', include('api.urls')), ] # api应用下的子路由 from django.conf.urls import url from . import views urlpatterns = [ # as_view() 本质拿到 view函数地址, # view内部通过dispatch分发请求给具体的(get|post|delete)方法处理请求 # 处理完后的响应结果会一层层返回 url(r'^books/$', views.BookView.as_view()), url(r'^books/(?P<pk>.*)/$', views.BookView.as_view()), ]
数据库迁移: >: python manage.py makemigrations >: python manage.py migrate
后台管理: from django.contrib import admin from . import models admin.site.register(models.Book) admin.site.register(models.User) # 创建超级用户 # >: python manage.py createsuperuser
views.py from django.views import View from django.http import JsonResponse from . import models class BookView(View): def get(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: # 通过是否有主键决定获取单个或是全部资源 book_dic_list = models.Book.objects.filter(pk=pk).values('name', 'price') if not book_dic_list: return JsonResponse({ 'status': 2, 'msg': 'pk值有误', 'results': {} }) return JsonResponse({ 'status': 0, 'msg': 'ok', 'results': book_dic_list[0] }) book_dic_list = models.Book.objects.all().values('name', 'price') if not book_dic_list: return JsonResponse({ 'status': 2, 'msg': '无数据', 'results': {} }) return JsonResponse({ 'status': 0, 'msg': 'ok', 'results': list(book_dic_list) })
开一个media窗口:
##### media工作目录:settings.py ```python # MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') ``` ##### 路由:主urls.py ```python from django.conf.urls import url, include from django.contrib import admin from django.views.static import serve from django.conf import settings urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/', include('api.urls')), url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}) ] ```
三.restful接口十条规范
1)一般都采用安全协议(接口都是操作数据的):https 2)体现接口的关键字:api https://api.oldboy.com https://www.oldboy.com/api 3)接口操作的数据称之为资源:采用资源名称的复数 https://api.oldboy.com/books/ https://api.oldboy.com/users/ 4)接口链接中不出现操作资源的动词,通过请求方式来决定操作资源的动作 https://api.oldboy.com/books/ get:获取所有 | post:增加一个 https://api.oldboy.com/books/(?P<pk>)/ get:获取一个 | put:整体修改一个(patch:局部修改一个) | delet:删除一个 5)资源数据有多版本时,接口可以做版本控制 https://api.oldboy.com/books/v1/ https://api.oldboy.com/v2/books/ 6)资源响应的限制条件:筛选、排序、限制... https://api.oldboy.com/books/?publish=1&ordering=-price&limit=3 7)响应状态码 网络状态码:2xx | 3xx | 4xx | 5xx 数据状态码(约定的):0 | 1 | 2 { 'status': 1, } -- SUCCESS(0, "查询成功") -- NODATA(1, "非正确,无数据,显示基本信息") -- FEAILED(2, "查询失败") 8)响应结果的信息描述: { 'status': 1, 'msg': 'login failed' } 9)响应的结果:get所有:所有资源 | get一个:一个资源 | post、put、patch:新增、修改的资源 | delete:不做任何返回 { 'status': 0, 'msg': 'ok', 'results': [登录的用户对象序列化结果] } 10)响应结果中有二次资源(用户头像:图片链接,用户详情:详情接口) 要表明请求二次资源的接口 注:通过 接口文档 告诉前台传递的必要和选填参数
四.
pip3 install djangorestframework
# 注册drf app NSTALLED_APPS = [ # ... 'rest_framework', ]
特点:
# 具体功能在具体模块下 from rest_framework.request import Request from rest_framework.response import Response from rest_framework.exceptions import APIException from rest_framework.filters import OrderingFilter from rest_framework.views import APIView from rest_framework.pagination import PageNumberPagination from rest_framework.settings import APISettings # 自定义drf配置 - 在自己的settings.py REST_FRAMEWORK = { # 自定义修改drf的配置们 }
五.源码分析APIView
1.django中的CBV:
url(r'^books/', views.Books.as_view()),
发现Books类调用as_view方法,自身类没有则找父类View, 进入源码发现它返回的是view的函数地址,
请求来了,则根据路由匹配去找视图函数,依然会找到父类View中的view函数,返回的是self.dispatch方法,self指的是Book
对象,自身没有,还是找父类中的dispatch方法;
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
首先判断方法名小写在不在django支持的这八种方法里面,如果在,并且Book类中有此方法,以反射获取方法值赋予句柄
handle,否则赋予 http_method_not_allowed方法的返回值给handle,状态码是405, 最后将handle返回;获取正确方法则进入视图
函数,并将响应结果逐层返回.
2.rest_framework中的CBV:
Book类继承的是APIView, 而APIView继承的是View, APIView是从rest_framework框架中导进来的,as_view方法返回的
是 csrf_exempt(view),也就是不会经过csrf中间件认证;
请求来了调用view函数,内部调用(APIView类的)dispatch函数完成请求分发;
request = self.initialize_request(request, *args, **kwargs)
dispatch中对request进行了一次封装,在initialize_request函数中返回的的是Request类的一个实例化,看它的__init__方法就
发现把原生django的request对象当做Request类对象的_request属性,所以封装后的request._request仍然是wsgi的request对象,
而request变成了rest_framework的request对象
dispatch还在self.initial中完成了三大认证: 认证/权限/频率
再将请求方式映射到视图函数的同名的方法中,得到响应后渲染,并将响应结果逐层返回
# 入口:APIView类的dispatch函数 self.response = self.finalize_response(request, response, *args, **kwargs) -> neg = self.perform_content_negotiation(request, force=True) -> renderers = self.get_renderers() -> self.renderer_classes -> APISetting:DEFAULT_RENDERER_CLASSES
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.renderers import JSONRenderer from rest_framework.renderers import BrowsableAPIRenderer class UserAPIView(APIView): # 局部配置:只有该视图类起作用 renderer_classes = [JSONRenderer] # 只提供JSON数据渲染 pass
# drf配置 REST_FRAMEWORK = { # 响应的渲染模块 'DEFAULT_RENDERER_CLASSES': [ 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ], }
# 入口:APIView类的dispatch函数 request = self.initialize_request(request, *args, **kwargs) -> parsers=self.get_parsers() -> self.parser_classes -> APISetting:DEFAULT_PARSER_CLASSES
局部配置:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.parsers import JSONParser from rest_framework.parsers import FormParser from rest_framework.parsers import MultiPartParser class UserAPIView(APIView): # 局部配置:只有该视图类起作用 parser_classes = [JSONParser] # 只提供JSON解析 pass
全局配置:
# drf配置 REST_FRAMEWORK = { # 响应的渲染模块 'DEFAULT_RENDERER_CLASSES': [ 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ], # 请求数据解析模块 'DEFAULT_PARSER_CLASSES': [ 'rest_framework.parsers.JSONParser', # 'application/json' 'rest_framework.parsers.FormParser', # 'application/x-www-form-urlencoded' 'rest_framework.parsers.MultiPartParser' # multipart/form-data ], }
请求数据解析位置:
请求的数据包:均解析到 request.data 中
请求的?文件参数(get请求url拼接参数):均解析到 request.query_params 中
八.响应模块
# 响应可以设置响应数据、响应网络状态码、响应头、响应数据类型等 data = { 'status': 0, 'msg': 'get ok', 'results': [], 'token': '123.12321.231' } return Response( data=data, status=status.HTTP_200_OK, headers={'Token': '123as.masd21.asd213sd'}, content_type='application/json' # 默认就是application/json )