Django Rest Framework(一)之 源码流程及示例
RESTFUL介绍
- 本质就是一个规范,翻译为"表现层状态转化","表现层"其实指的是"资源"(Resources)的"表现层"。
- REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的信息实体,可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
RESTFUL设计规范
- 推荐使用cbv
- API与用户的通信协议,建议总是使用HTTPs协议。
- 域名
- https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)
- https://example.org/api/ API很简单
-
版本
URL,如:https://api.example.com/v1/
-
路径,视网络上任何东西都是资源,均使用名词表示(可复数)
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees - method
-
GET :从服务器取出资源(一项或多项)
-
POST :在服务器新建一个资源
-
PUT :在服务器更新资源(客户端提供改变后的完整资源)
-
PATCH :在服务器更新资源(客户端提供改变的属性)
-
DELETE :从服务器删除资源
- 过滤,通过在url上传参的形式传递搜索条件
-
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:指定筛选条件
- 返回结果
-
GET /collection:返回资源对象的列表(数组)
-
GET /collection/resource:返回单个资源对象
-
POST /collection:返回新生成的资源对象
-
PUT /collection/resource:返回完整的资源对象
-
PATCH /collection/resource:返回完整的资源对象
-
DELETE /collection/resource:返回一个空文档
-
- 状态码
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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
源码流程及基础认证示例
源码流程(以一个示例说明,如下,views下的api_view类)
from django.views import View from rest_framework.views import APIView #通过查看源码发现,APIView 继承了django.views 下的View from rest_framework.authentication import BaseAuthentication from rest_framework import exceptions 十三、#自定义一个认证实例 class MyAuthentication(object): def authenticate(self,request):
#做用户认证,例如可以去数据库获取用户名和密码进行校验,这里只做简单示例 token = request._request.GET.get('token') #这里获取原生的request里面的值 if not token: raise exceptions.AuthenticationFailed("用户认证失败") return ('xxx',None) def authenticate_header(self,*args): pass class v1_View(APIView): #① 自定义的函数执行类 authentication_classes = [MyAuthentication,] #引用上面的认证实例,这里对应下面的第⑦步骤
def get(self,request,*args,**kwargs): #self.dispatch() #② 默认后台执行 ret = { 'code':1000, 'msg':'success' } return HttpResponse(json.dumps(ret),status=201) def post(self, request, *args, **kwargs): return HttpResponse("POST") def put(self, request, *args, **kwargs): return HttpResponse("PUT") def delete(self, request, *args, **kwargs): return HttpResponse("DELETE")
dispatch源码
def dispatch(self, request, *args, **kwargs): #③、 self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) #④、 self.request = request #这里的request为第④步处理后的request self.headers = self.default_response_headers # deprecate? ⑨、#执行函数 try: self.initial(request, *args, **kwargs) #⑩ # 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) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) #这里的request为上面经过self.initialize_request实例化的request,并将这个request传递给views里定义的各个方法 except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
步骤④后续,initialize_request函数源码
def initialize_request(self, request, *args, **kwargs): parser_context = self.get_parser_context(request) ⑤、 return Request( request, #这里为传入的原生的request parsers=self.get_parsers(), authenticators=self.get_authenticators(), ⑥、 negotiator=self.get_content_negotiator(), parser_context=parser_context )
查看第④步骤下Request的源码(这里包含众多request里要获取的值)
class Request(object): def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): self._request = request #这里为原生的request,通过self_request获取 self.parsers = parsers or () self.authenticators = authenticators or () #获取认证类的实例化对象 self.negotiator = negotiator or self._default_negotiator() self.parser_context = parser_context self._data = Empty self._files = Empty self._full_data = Empty self._content_type = Empty self._stream = Empty if self.parser_context is None: self.parser_context = {} self.parser_context['request'] = self self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET force_user = getattr(request, '_force_auth_user', None) force_token = getattr(request, '_force_auth_token', None) if (force_user is not None or force_token is not None): forced_auth = ForcedAuthentication(force_user, force_token) self.authenticators = (forced_auth,)
................
................
................
步骤⑥后续 get_authenticators函数
def get_authenticators(self): return [auth() for auth in self.authentication_classes] #⑦、
步骤⑦后续
class APIView(View): # The following policies may be set at either globally, or per-view. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES parser_classes = api_settings.DEFAULT_PARSER_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES #⑧、 throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS metadata_class = api_settings.DEFAULT_METADATA_CLASS versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
第⑩步骤后续
def initial(self, request, *args, **kwargs): #传入的request为经过第④步处理后的request self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted self.perform_authentication(request) #十一 self.check_permissions(request) self.check_throttles(request)
self.perform_authentication源码
def perform_authentication(self, request): #执行 user函数 request.user
第十一步后续
Request方法, @property def user(self): if not hasattr(self, '_user'): 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 self._not_authenticated() #如果没有登录,就会报异常,这里也是调用没有验证的函数
说明
①、#默认情况下会继承django的View,但是引入了rest_framework下的APIView,查看源码发现,它同样继承django下的View,所以这里写APIView,除了会继承APIView还会继承View ②、根据cbv执行流程来看,请求经过url和view后,执行get或post等方法之前,会先执行self.dispatch()方法,查看dispatch源码, ③、#执行函数方法(get,post...)之前所做的操作 ④、#这里self.initialize_request传入的request为原生的request,点击查看self.initialize_request源码 ⑤、#对原生的request进行加工,传入Request函数,点击可查看源码 ⑥、#这里调用get_authenticators(),首先会从views下自己定义的v1_view类里找有没有这个函数,如果没有定义,会去父类查找,点击查看get_authenticators()源码,([自己定义的认证类实例化()]),放到request,回到第④步,此时的request包含认证的实例对象和原生的request ⑦#这里返回一个列表,列表的内容为类的实例化的对象,如[foo(),bar()],这里的authentication_classes首先会从自己定义的v1_view里查找。如果没有就去父类找,先点击查看self.authentication_classes的源码,完后进行第⑧步后,然后放到Request函数,回到dispatch,进行第⑨步,此时的request封装了原生的request和authentication对象 ⑧、#这里读取rest_framework的配置文件,如果自己定义了(v1_view类),则就会使用自己定义的类,否则返回到步骤⑦, ⑨、#执行函数 ⑩、这里的request为第④步经过处理后的request,这里initial函数先从自定义的view里找,没有就去父类找,看initial源码
十一、经过处理后的request,这里返回执行request.user类,查看request.user类
十三 、#执行这个方法,验证用户是否登录,所以判定如果自定义认证时,需要加上这个方法
认证示例
views.py
from django.views import View from rest_framework.views import APIView #通过查看源码发现,APIView 继承了django.views 下的View from rest_framework.authentication import BaseAuthentication from rest_framework import exceptions
#自定义一个认证实例 class MyAuthentication(object): def authenticate(self,request): token = request._request.GET.get('token') if not token: raise exceptions.AuthenticationFailed("用户认证失败") return ('xxx',None) def authenticate_header(self,*args): pass class v1_View(APIView): authentication_classes = [MyAuthentication,] #引用上面的认证实例,通过源码流程原理可知道为什么引用 def get(self,request,*args,**kwargs): self.dispatch() ret = { 'code':1000, 'msg':'success' } return HttpResponse(json.dumps(ret),status=201) def post(self, request, *args, **kwargs): return HttpResponse("POST") def put(self, request, *args, **kwargs): return HttpResponse("PUT") def delete(self, request, *args, **kwargs): return HttpResponse("DELETE")