3月5日学习内容整理:restframework的规范,其中的认证功能
一、restful是什么
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
- 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)
django的restframework框架就是做前后端分离开发的,就像是form组件,session等就是django为我们提供的一个功能组件
前后端分离开发,后端为前端提供URL,也可以说是API/接口开发,永远返回Httpresponse,前端发ajax请求提交获取数据
二、restful规范
与其说是规范,也就是建议,大家都这么开发
1、推荐使用https,更安全
2、域名的创建
》》》若用子域名的方式的话需要解决跨域的问题
》》》或者用相同的域名后面加上api:https://example.org/api/
3、版本
在URL中加入版本信息,以便区分,https://api.example.com/v1/
4、尽量使用名词,路径,视网络上任何东西都是资源,均使用名词表示(可复数)
5、要根据不同的请求method做出不同的操作
- GET :从服务器取出资源(一项或多项)
- POST :在服务器新建一个资源
- PUT :在服务器更新资源(客户端提供改变后的完整资源)
- PATCH :在服务器更新资源(客户端提供改变的属性)
- DELETE :从服务器删除资源
6、过滤,利用URL中的数据传递查询条件
- https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
7、状态码:
状态码是不能完全的表达响应信息的,通常我们都要在返回对象中加入code,从而能表示出更多的响应信息,给用户提供更多的提示信息
也就是状态码+code的形式
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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
8、错误处理,状态码是4xx时,应返回错误信息,error当做key。
9、返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
》》GET
/
collection:返回资源对象的列表(数组)
》》GET
/
collection
/
resource:返回单个资源对象
》》POST
/
collection:返回新生成的资源对象
》》PUT
/
collection
/
resource:返回完整的资源对象
》》PATCH
/
collection
/
resource:返回完整的资源对象
》》DELETE
/
collection
/
resource:返回一个空文档
10、返回对象中也尽可能的提供链接,指向别的API,用户使用起来更方便
Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么
{"link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" }}
三、restframework认证的源码流程
1、怎么写:
from django.views import View from rest_framework.views import APIView from rest_framework.authentication import BasicAuthentication from rest_framework import exceptions from rest_framework.request import Request class MyAuthentication(object): # 必须写这个方法 def authenticate(self, request): token = request._request.GET.get('token') # 获取用户名和密码,去数据校验 if not token: raise exceptions.AuthenticationFailed('用户认证失败') return ("alex", None) # 这个方法也必须要有 def authenticate_header(self, val): pass class DogView(APIView): authentication_classes = [MyAuthentication, ] def get(self, request, *args, **kwargs): print(request) print(request.user) ret = { 'code': 1000, 'msg': 'xxx' } return HttpResponse(json.dumps(ret), status=201) def post(self, request, *args, **kwargs): return HttpResponse('创建Dog') def put(self, request, *args, **kwargs): return HttpResponse('更新Dog') def delete(self, request, *args, **kwargs): return HttpResponse('删除Dog')
2、具体的源码流程:
有三个类:自定义View类,以下简称自定义;APIView类(djangorestframework),以下简称API;View类(django),以下简称V
继承关系,自定义继承API,API继承V
注意,以下涉及到的属性和方法都要先从自定义去找,再从API找,再从V找,因为是自定义最先调用的as_view方法,也就是入口
1、URL路由匹配成功后首先执行as_view方法,实际上执行的是API的as_view,其中又用super方法执行V的as_view,再是执行V的view方法,再是执行dispatch方法,注意这里就会执行API中的dispatch方法如下第一图,
2、首先是将传入的原生request重新赋值,调用API的initialize_request方法,得到一个Request对象如下第二图,这个request封装了两个属性如下第三图,request._request指的就是原生的request,request.authenticators则是一个列表,里面是SessionAuthentication BasicAuthentication 这两个类的实例对象,这两个类是restframework里默认的,就是用来做认证的类,这两个类是API中的authentication_classes参数里定义的
3、将request重新封装后initialize_request函数结束后继续向下执行如下第一图,执行API的initial方法,在initial中执行self.perform_authentication(request)方法,在这个方法中运行request.user方法,这个request就是dispatch方法中重新封装的request也就是Request对象的user属性继而执行_authenticate方法,这个方法就会循环第2步中封装的request的authenticators属性,也就是存放着两个认证类的实例对象的列表,依次执行两个实例对象的authenticate方法,这个方法就是具体来做认证操作的
4、API的initial方法执行完后,也就代表认证通过,继续向下就会利用反射去执行不同请求method对应的函数了如下第一图
所以依据源码流程,我们自定义认证的话首先就要在自定义view类中定义好authentication_classes参数,不要去API中找,直接用自定义view类的,然后再自定义认证类,必须保证有authenticate方法,就在这个方法里做认证操作就可以了,一定要注意认证函数authenticate里的request已经是重新封装过的了,也就是Request对象
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs # 是Request的对象,self._request就是原生的request # self.authenticators就是是一个列表,里面是SessionAuthentication BasicAuthentication 这两个类的对象 request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs)7 # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, # 这个request是原生的request parsers=self.get_parsers(), # 是一个列表,里面是SessionAuthentication BasicAuthentication 这两个类的对象 authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context )
class Request(object): def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): self._request = request self.authenticators = authenticators or ()
@property def user(self): if not hasattr(self, '_user'): with wrap_attributeerrors(): self._authenticate() return self._user
def _authenticate(self): for authenticator in self.authenticators: try: user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return