一、rest api
a、api就是接口
如: - http://www.oldboyedu.com/get_user/
- http://www.oldboyedu.com/get_users/
b、api的两个用途
1、为别人提供服务
2、前后端分离
二、restful
a、--字面意思:表征状态转移
b、面向资源编程,对互联网上的任意东西都视为资源
如:- http://www.oldboyedu.com/get_user/
- http://www.oldboyedu.com/get_img/1.png
三、restful规范
a、 ---URL
b、 ---url名词
路径,视网络上任何东西都是资源,均使用名词表示(可复数) https://api.example.com/v1/zoos https://api.example.com/v1/animals https://api.example.com/v1/employee
c、 ---status状态码
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) 204 NO CONTENT - [DELETE]:用户删除数据成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
d、---提交方式
GET :从服务器取出资源(一项或多项)
POST :在服务器新建一个资源
PUT :在服务器更新资源(客户端提供改变后的完整资源)
PATCH :在服务器更新资源(客户端提供改变的属性)
DELETE :从服务器删除资源
e、 ---错误信息
状态码是4xx时,应返回错误信息,error当做key。 { error: "Invalid API key" }
f、 ---版本
g、 ---Hypermedia link,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
h、---域名
j、---过滤,通过在url上传参的形式传递搜索条件
k、 ---返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
GET /collection:返回资源对象的列表(数组) GET /collection/resource:返回单个资源对象 POST /collection:返回新生成的资源对象 PUT /collection/resource:返回完整的资源对象 PATCH /collection/resource:返回完整的资源对象 DELETE /collection/resource:返回一个空文档
rest_Framework的规范:
按顺序:它的method的不同,原来没有考虑,原来是url区分,现在通过method来区分,method的不同提交方式不同,紧接着一般是面向资源的就是把url变成名词,接下就是返回值,以前没有考虑状态码,现在有考虑状态码。(一般有get,post方法,还有put,delete等方法)
四、 基于Django Rest Framework框架实现
a、安装:pip3 install djangorestframework -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com
五、什么是RESTful
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
- 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)
a、 django的中间件比rest_framework执行的早
b、 认证的功能放到中间件也是可以做的
c、认证一般做,检查用户是否存在,如果存在request.user/request.auth;不存在request.user/request.auth=None
d、认证小总结:
---类:authenticate/authenticate_header ---返回值:None,元组(user,auth),异常 ---配置: ---视图: class IndexView(APIView): authentication_classes = [MyAuthentication,] ---全局 REST_FRAMEWORK = { 'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_TOKEN': None, "DEFAULT_AUTHENTICATION_CLASSES": [ # "app02.utils.MyAuthentication", ], }
六、权限
a、权限才真正的做request.user/request.auth拿到它们做是否访问的判断
b、权限小总结:
---类: has_permission/has_object_permission ---返回值:True、False、exceptions.PermissionDenied(detail="错误信息") ---配置: ---视图: class IndexView(APIView): permission_classes = [MyPermission,] ---全局: REST_FRAMEWORK = { "DEFAULT_PERMISSION_CLASSES": [ # "app02.utils.MyAuthentication", ], }
七、限制访问的频率
限制访问频率的应用:
a、对匿名用户进行限制,每个用户1分钟允许访问10次
在这里用唯一标识:self.get_ident()
b、限制访问的频率小总结:
---类: allow_request/wait PS: scope = "wdp_user" ---返回值:True、False ---配置: ---视图: class IndexView(APIView): throttle_classes=[AnonThrottle,UserThrottle,] def get(self,request,*args,**kwargs): self.dispatch return Response('访问首页') ---全局: REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES":[ ], 'DEFAULT_THROTTLE_RATES':{ 'wdp_anon':'5/minute', 'wdp_user':'10/minute', } }
八、认证、权限、限制访问频率返回结果的比较
1、认证、权限、限制访问频率这三个返回的结果:
a、认证返回的三种结果:
a、None
b、元组(user,auth)
c、异常 raise APIException(...)
b、 权限的返回值三种结果:
a、True 有权限
b、False 没权限
c、异常
c、 限制访问的频率返回值的两种结果:
a、True
b、False
实例化: v1 = ["view.xxx.path.Role","view.xxx.path.Group",] 可以循环,循环出来的每一个不能实例化 如果把v1循环弄成每一个对象列表,通过rsplit切割,在通过importlib.import_module拿到每一个路径,在通过getattr把它的类名拿过来, 这个类加括号就是实例化想 for item in v1: m = importlib.import_module('view.xxx.path') cls = getattr(m,'Role') cls() from view.xxx.path import Role,Group v2 = [Group,Role] 这个可以循环每一个实例化 for item in v2: #循环V2的每一个元素加括号,就是实例化 item()
九、Django rest_Framework框架
a、 ----为什么用Django rest_Framework框架?
----首先没有Django rest_Framework框架用django也是可以做出来的,只不过它为我们提供一些API常用的功能,比如:(认证,权限,限流,有了这些我们只需要写个类已配置,
它就能当都市图用,还能全局配置,如果自己写还得写中间件,写装饰器来实现,通过Django rest_Framework框架,他已经把规则写好,只需要写类,只需实现方法,返回值就可以)
实现了一部分功能。 ----设计比较好 ----单独视图+全局配置 =>Dajngo中间件(importlib/反射)=>动态配置课扩展(短信,邮件,微信等提醒)
b、Django rest_Framework原理?
先开始在路由,路由.as_view,
点击as_view
请求进来,走完以上,才走self.dispatch()
走self.dispatch()流程如下地址:http://www.cnblogs.com/mengqingjian/p/8419563.html
十、版本
a、根据url的不同来来操作,版本控制
先在setting中注册
十一、rest framework解析器
a、请求的数据进行解析:请求体进行解析。表示服务端可以解析的数据格式的种类。
Content-Type: application/url-encoding..... request.body request.POST Content-Type: application/json..... request.body request.POST 客户端: Content-Type: application/json '{"name":"alex","age":123}' 服务端接收: 读取客户端发送的Content-Type的值 application/json parser_classes = [JSONParser,] media_type_list = ['application/json',] 如果客户端的Content-Type的值和 application/json 匹配:JSONParser处理数据 如果客户端的Content-Type的值和 application/x-www-form-urlencoded 匹配:FormParser处理数据 配置: 单视图: class UsersView(APIView): parser_classes = [JSONParser,] 全局配置: REST_FRAMEWORK = { 'VERSION_PARAM':'version', 'DEFAULT_VERSION':'v1', 'ALLOWED_VERSIONS':['v1','v2'], # 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.HostNameVersioning" 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning", 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', ] }
十二、 rest framework序列化+Form
a、序列化:
对象 -> 字符串 序列化
字符串 -> 对象 反序列化
b、目的:
解决QuerySet序列化问题
十三、为什么要前后端分离
a、因为前端它有自己框架,这样它的效率就非常高
b、不做前后端分离,公司如果既有客户端,又有app这种情况下你就的写两遍
十四、django restful框架好处
帮助我们写了好多组件比如:
a、认证:有类,类中的方法authenticate/authenticate_header,它的返回值有None,元组,异常。如果返回值为None那就不管,它是匿名用户。
b、权限:有类,类中的方法:has_permission
c、节流:有类,类的方法:allow_request,在这里allow_request来表示限制,它是通过IP来限制,
它的内部原理是:默认是IP,用户来用IP,这个IP可能代理IP,IP或者是代理IP拿着请求头默认放到大家能够的缓存中
去,每一个人的IP为p,后面的那个列表存它的方位时间,每一次请求进来获取它的当前时间根据时间的个数来比较,在
比较的过程中看看把不符合时间的个数来进行比较,能访问就继续,不能访问就不要继续。
d、版本:是url和hostname,他们两个钟每一个都有两个方法一个是帮你拿版本另一个是帮你反向生成url
e、解析器:用户发过来的请求体数据进行操作
f、序列化:两个功能:序列化,校验
十五、分页
分页的三种情况:
a、记录当前访问页的数据id
b、最多显示120页
c、对页码进行加密
a、基于limit offset做分页
class P1(LimitOffsetPagination): max_limit = 3 default_limit = 2 limit_query_param = 'limit' offset_query_param = 'offset' class IndexView(views.APIView): def get(self,request,*args,**kwargs): user_list = models.UserInfo.objects.all() p1 = P1() page_user_list = p1.paginate_queryset(queryset=user_list, request=request, view=self) ser = IndexSerializer(instance=page_user_list, many=True) return Response(ser.data) # 不含上一页和下一页 # return p1.get_paginated_response(ser.data) # 含上一页和下一页 class IndexView(views.APIView): def get(self,request,*args,**kwargs): ret = BaseResponse() try: user_list = models.UserInfo.objects.all() p1 = P1() page_user_list = p1.paginate_queryset(queryset=user_list,request=request,view=self) ser = IndexSerializer(instance=page_user_list,many=True) ret.data = ser.data ret.next = p1.get_next_link() except Exception as e: ret.code= 1001 ret.error = 'xxxx错误' return Response(ret.__dict__)
b. 基于页码的分页
class P2(PageNumberPagination): # 每页显示的数据条数 max_page_size = 5 page_size = 2 page_size_query_param = 'size' # 页码 page_query_param = 'page'
十六、视图
1、APIView
class IndexView(views.APIView): def get(self, request, *args, **kwargs): user_list = models.UserInfo.objects.all() ser = IndexSerializer(instance=user_list,many=True) return Response(ser.data)
2、GenericAPIview(APIView)
3、GenericViewSet(ViewSetMixin,generics.GenericAPIView)
路由修改: urlpatterns = [ url(r'^index/$', views.IndexView.as_view({'get':'list','post':'create'})), url(r'^index/(?P\d+)$ ', views.IndexView.as_view({'get':'retrieve'})), ] 视图修改: class IndexView(viewsets.GenericViewSet): def list(self,request,*args,**kwargs): pass # 获取列表信息 def retrieve(self, request, *args, **kwargs): pass # 获取单条数据 def create(self,request, *args, **kwargs): pass 自定义: 增 POST /users/ 删 DELETE /users/1/ 改 PUT /users/1/ patch /users/1/ 查 GET /users/ GET /users/1/ urlpatterns = [ url(r'^index/$', views.IndexView.as_view()), url(r'^index/(?P\d+)$ ', views.IndexView.as_view()), ] class IndexView(views.APIView): def get(self,request,*args,**kwargs): pk = kwargs.get('pk') if pk: pass # 获取单条信息 else: pass # 获取列表信息 def post(self,request,*args,**kwargs): pass def put(self,request,*args,**kwargs): pass def patch(self,request,*args,**kwargs): pass def delete(self,request,*args,**kwargs): pass
4、ModelViewSet
ModelViewSet(mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin,GenericViewSet) class IndexView(ModelViewSet):
十七、 渲染器
看到的页面时什么样子的,返回数据。
renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
十八、跨域
a、 浏览器的同源策略
1、 ----对ajax请求进行阻拦
2、 ----对href属性读不阻拦
xhr=new XMLHttpRequest
xhr.open...
xhr.send(...)
b、 解决方案:
---JSONP
点击按钮:
动态添加一个
c、删除
1. 跨域 浏览器的同源策略 - 对ajax请求进行阻拦 - 对href属性都不阻拦 xhr = new XMLHttpRequest(); xhr.open... xhr.send(...) 解决方案: - JSONP 点击按钮: 动态添加一个 删除 PS: 必问 客户端浏览器: - jsonp(pythonav) Pythondv网站-政府: - jsonp(pythonav) Pythonav网站: - { 'code':1111, 'list':[ ... ... ... ] } - CORS 客户端浏览器: - $.ajax(pythonav) Pythondv网站-政府: - $.ajax(pythonav) Pythonav网站: - { 'code':1111, 'list':[ ... ... ... ] } - 响应头 a. 简单请求(非常好) A网站: "button" value="获取用户数据" οnclick="getUsers()"> 服务商: class UsersView(views.APIView): def get(self,request,*args,**kwargs): ret = { 'code':1000, 'data':'老男孩' } response = JsonResponse(ret) response['Access-Control-Allow-Origin'] = "*" return response b. 复杂请求(性能上的损耗,options预检,真实的请求) A网站: "button" value="获取用户数据" οnclick="getUsers()"> 服务商: class UsersView(views.APIView): def get(self,request,*args,**kwargs): ret = { 'code':1000, 'data':'老男孩' } response = JsonResponse(ret) response['Access-Control-Allow-Origin'] = "*" return response def post(self,request,*args,**kwargs): print(request.POST) ret = { 'code':1000, 'data':'老男孩' } response = JsonResponse(ret) response['Access-Control-Allow-Origin'] = "*" return response def options(self, request, *args, **kwargs): # self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com") # self.set_header('Access-Control-Allow-Headers', "k1,k2") # self.set_header('Access-Control-Allow-Methods', "PUT,DELETE") # self.set_header('Access-Control-Max-Age', 10) response = HttpResponse() response['Access-Control-Allow-Origin'] = '*' response['Access-Control-Allow-Headers'] = 'h1' # response['Access-Control-Allow-Methods'] = 'PUT' return response 2. Vue+rest示例 前端:vue 修改源: npm config set registry https://registry.npm.taobao.org 创建脚手架: vue init webpack Vue项目名称 # Install vue-router? Yes 插件: axios,发送Ajax请求 vuex,保存所有组件共用的变量 vue-cookies,操作cookie 流程: 1. 创建脚手架 2. # 用于点击查看组件"/index">首页 # 组件显示的位置3. 写路由 import Vue from 'vue' import Router from 'vue-router' import Index from '@/components/Index' import Login from '@/components/Login' import Course from '@/components/Course' import Micro from '@/components/Micro' import News from '@/components/News' import CourseDetail from '@/components/CourseDetail' import NotFound from '@/components/NotFound' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'index', component: Index }, { path: '/index', name: 'index', component: Index }, { path: '/course', name: 'course', component: Course }, { path: '/course-detail/:id/', name: 'courseDetail', component: CourseDetail }, { path: '/micro', name: 'micro', component: Micro }, { path: '/news', name: 'news', component: News }, { path: '/login', name: 'login', component: Login }, { path: '*', component: NotFound } ], mode: 'history' }) 4. 写组件 5. ajax请求:axios npm install axios main.js import Vue from 'vue' import App from './App' import router from './router' import axios from 'axios' Vue.prototype.$axios = axios Vue.config.productionTip = false ... 组件使用: this.$axios.request({ url: 'http://127.0.0.1:8000/login/', method: 'POST', data: { username: this.username, password: this.password }, responseType: 'json' }).then(function (response) { console.log(response.data) that.$router.push('/index') }) PS:重定向 that.$router.push('/index') 6. vuex npm install vuex main.js import Vue from 'vue' import App from './App' import router from './router' import axios from 'axios' import store from './store/store' # vuex Vue.prototype.$axios = axios Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', store, # vuex router, components: { App }, template: '登录页面
"text" v-model="username" placeholder="用户名"> "text" v-model="password" placeholder="密码"> "doLogin">提交' }) src/store/store.js import Vue from 'vue' import Vuex from 'vuex' import Cookie from 'vue-cookies' Vue.use(Vuex) export default new Vuex.Store({ // 组件中通过 this.$store.state.username 调用 state: { username: Cookie.get('username'), token: Cookie.get('token') }, mutations: { // 组件中通过 this.$store.commit(参数) 调用 saveToken: function (state, data) { state.username = data.username state.token = data.token Cookie.set('username', data.username, '20min') Cookie.set('token', data.token, '20min') }, clearToken: function (state) { state.username = null state.token = null Cookie.remove('username') Cookie.remove('token') } } }) 7. vue-cookies npm install vue-cookies Cookie.get('username') Cookie.set('username', data.username, '20min') Cookie.remove('username') src/store/store.js import Vue from 'vue' import Vuex from 'vuex' import Cookie from 'vue-cookies' # vue-cookies Vue.use(Vuex) export default new Vuex.Store({ // 组件中通过 this.$store.state.username 调用 state: { username: Cookie.get('username'), # vue-cookies token: Cookie.get('token') # vue-cookies }, mutations: { // 组件中通过 this.$store.commit(参数) 调用 saveToken: function (state, data) { state.username = data.username state.token = data.token Cookie.set('username', data.username, '20min') # vue-cookies Cookie.set('token', data.token, '20min') }, clearToken: function (state) { state.username = null state.token = null Cookie.remove('username') # vue-cookies Cookie.remove('token') } } }) 8. 路由 # 定义路由 { path: '/course-detail/:id/', name: 'courseDetail', component: CourseDetail }, { path: '/login', name: 'login', component: Login }, { path: '*', component: NotFound } # router-link参数 "{'path':'/course-detail/'+item.id }">{{item.name}} "/index">首页 # 获取传过来的参数 this.$route.params.id # 重定向 this.$router.push('/index')