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 :从服务器删除资源
  • 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")