REST framework 说明和源码剖析

REST framework 说明与源码剖析

 

REST的出处

Roy Fielding的毕业论文。

论文地址:Architectural Styles and the Design of Network-based Software Architectures

REST章节:Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST)

 

REST名称

REST:REpresentational State Transfer = 直接翻译:表现层状态转移。全称是 Resource Representational State Transfer:通俗来讲就是:资源在网络中以某种表现形式进行状态转移。分解开来:

Resource:资源,即数据(前面说过网络的核心)。比如 newsfeed,friends等;
Representational:某种表现形式,比如用JSON,XML,JPEG等;
State Transfer:状态变化。通过HTTP动词实现。

  

 REST的优点

 在互联网开始初期,网页都是前端和后端融在一起的,在这之前的桌面时代问题不大,但是随着近几年互联网的飞速发展,各种类型的客户不断出现,RESTful API可以通过一套统一的接口对不同的客户端提供数据,况且,现在许多平台不需要提供前端页面,只是需要提供数据而已(微信公众号平台等),于是RESTful便是他们最好的选择;REST架构如下:

很明显,如果我们使用上面的这种结构,那么就很容易做到前后端分离的效果,各种客户端可以独自完成前端功能和页面(PC端可以使用vue来搭建项目),需要使用数据的额时候则可以通过api借口向后台获取数据进行渲染

 

什么是RESTful

REST与技术无关,代表的是一种软件架构风格;

REST从资源的角度审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态,将一切数据视为资源是REST与其它架构风格最基本的属性;

REST本身不实用,使用的是如何设计RESTful API(REST风格的网络接口);

 

如何设计RESTful API

  • 域名 URL root:
    • http://api.example.com           尽量将API部署在相同域名(否则会存在跨域问题);
    • http://example.org/api/       API;
  • 版本 API versioning:
    • 可以放在URL里面,可以刻用HTTP的header
      • /api/v1/          放在url中;
      • 请求头           跨域时,应发多次请求(解决方案:cors);
  • 路径URL应该使用名词而不是动词,且土建用复数
    • api.example.com/v1/animals        获取动物列表;
    • api.example.com/v1/friends          获取某人的好友列表;
  • 同一个路径使用HTTP协议里的动词来实现资源的增、删、改、查等操作
    • GET 用来获取资源,返回资源对象的列表(数组);
    • POST 用来新建资源,返回单个资源对象;
    • PUT 用来跟新资源(整条数据都跟新),返回完整的资源对象;
    • PATCH 用来跟新资源(更新一条数据的部分信息),返回完整的资源对象;
    • ELETE 用来删除资源,返回一个空文档;

  • 过滤,通过在url传参的形式传递搜索条件
    • api.example.com/v1/friends?limit=10        制定返回记录的数量
    • api.example.com/v1/friends?offset=10      制定返回记录的开始位置
    • api.example.com/v1/friends?lfriend_type_id=1       指定筛选条件
  • 使用正确的HTTP Status Code表示访问状态(详细点击
    • 200 OK - [GET]:服务器成功返回用户请求的数据。
    • 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功;
    • 204 NO CONTENT - [DELETE]:用户删除数据成功;
    • 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误;
    • 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误);
    • 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的;
    • 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录
    • 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功;

 基于Django项目创建の基本流程

路由系统

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'users/', views.UserInfo.as_view())
]

  

CBV视图

from rest_framework.views import APIView
from django.http import HttpResponse

class UserInfo(APIView):
    def dispatch(self, request, *args, **kwargs):
        """
        请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法
        注意:APIView中的dispatch方法有好多好多的功能
        """
        return super().dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return HttpResponse("...")

    def post(self, request, *args, **kwargs):
        return HttpResponse("...")

# 这里只是做简单的介绍,并不涉及业务逻辑的处理

上面就是就是REST framework框架的基本流程,重要的功能都是在APIView中的dispathch中触发;

 

 REST Framework框架的流程(dispath起)

代码执行顺序从上往下(总体流程,并非每行代码都做解释)

  • initialize_request   重新封装Request对象,下面是封装的内容
    • reqeust,                                                                 封装原request;
    • parsers=self.get_parsers(),                                   解析用户请求数据;
    • authenticators=self.get_authenticators(),              认证相关
    • negotiator=self.get_content_negotiator(),        选择相关
    • parser_context=parser_context                            封装self和参数
  • self.initial(request, *args, **kwargs)     进行初始化
    • version, scheme = self.determine_version(request, *args, **kwargs)     获取版本信息
    • self.perform_authentication(request)       用户验证
    • self.check_permissions(request)        权限验证
    • self.check_throttles(request)        访问次数控制

配置书写的位置

# 在settings中配置这样的字典,里面存放的是我们自定义的配置,可以是版本相关、验证、权限、限制访问次数之类的相关信息;
REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
    "VERSION_PARAM":"version",
    "DEFAULT_VERSION":'v1',
    "ALLOWED_VERSIONS":['v1','v2'],

    "UNAUTHENTICATED_USER":None,
    "UNAUTHENTICATED_TOKEN":None,
    "DEFAULT_AUTHENTICATION_CLASSES":[
        #"app01.views.CustomAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES":[
        ""
    ],
    "DEFAULT_THROTTLE_RATES": {
        'anon': '5/m'
    }

}

  

获取版本信息

 首先去配置中查看  DEFAULT_VERSIONING_CLASS 中读取类(from rest_framework.versioning import BaseVersioning可以从这个路径里面查看如何获取版本)类的路径(用点分隔,最后一个位置应该是类的名字),读取后实例化该类,并执行类中的determine_version方法,该方法需要返回版本的信息,我们可以从上面的路径中选择出一个类写入配置中,然后按照它的要求在前端进行版本信息的传输,那么默认有几个关于获取版本信息的配置呢,它们又是什么原理呢?下面我们先总体说明下流程后再分析获取版本内容的代码

步骤一:

initial中调用  self.determine_version()方法,拿到一个元组(存放的是版本信息和生成该版本信息的类的对象)后将两个内容分别赋值给新Request中的 version以及versioning_scheme属性

1 def initial(self, request, *args, **kwargs):
2     # Determine the API version, if versioning is in use.
3     version, scheme = self.determine_version(request, *args, **kwargs)
4     request.version, request.versioning_scheme = version, scheme
initial部分代码

步骤二: 

在determine_version() 方法中判断是否启用版本相关的配置(self.versioning_class),如果没有启用,那么返回元组(None, None),否则的话实例化配置中存放的类,并执行类中的determine_version( )方法

1 versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
self.versioning_class属性
 1 def determine_version(self, request, *args, **kwargs):
 2     """
 3     If versioning is being used, then determine any API version for the
 4     incoming request. Returns a two-tuple of (version, versioning_scheme)
 5     """
 6     # 如果没有启用版本配置,那么返回元组(None, None)
 7     if self.versioning_class is None:
 8         return (None, None)
 9     # 否则的话实例化配置中存放的类,并执行类中的determine_version()方法
10     scheme = self.versioning_class()
11     # 执行结束后,该方法会返回版本的信息,并和生成该版本信息的类的对象一起封装成元组后返回
12     return (scheme.determine_version(request, *args, **kwargs), scheme)
determine_version 方法的说明

步骤三:

我们写在配置中的类实例化后执行的 determine_version方法应该做些什么呢,我们可以进去看一下,我们可以打印self.versioning_class来获取下默认的版本配置里面的内容(可能是空),后面的用户验证或者权限、限制访问次数等都是这个套路,里面存放的其实是路劲的字符串,我们可以通过import的方式后按住Ctrl和鼠标左键配合,进入到这个文件中查看各个类的代码

from rest_framework.versioning import BaseVersioning
 1 class BaseVersioning(object):
 2     """
 3     该类主要用来被作为基类继承并实现;
 4     default_version: 配置默认的版本信息?
 5     allowed_versions: 配置允许的版本信息
 6     version_param: 前端传版本信息时所使用的key?
 7     """
 8     default_version = api_settings.DEFAULT_VERSION
 9     allowed_versions = api_settings.ALLOWED_VERSIONS
10     version_param = api_settings.VERSION_PARAM
11 
12     def determine_version(self, request, *args, **kwargs):
13     # 需要实现这个方法,否则将会报错
14         msg = '{cls}.determine_version() must be implemented.'
15         raise NotImplementedError(msg.format(
16             cls=self.__class__.__name__
17         ))
18 
19     def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
20     # 调用 _reverse 方法,用来反向生成 URL
21         return _reverse(viewname, args, kwargs, request, format, **extra)
22 
23     def is_allowed_version(self, version):
24     # 判断是否是合法的版本,如果没有配置allowed_versions,则合法
25     # 否则判断该版本是否为默认的版本或在允许的版本中
26         if not self.allowed_versions:
27             return True
28         return ((version is not None and version == self.default_version) or
29                 (version in self.allowed_versions))
BaseVersioning介绍
 1 class AcceptHeaderVersioning(BaseVersioning):
 2     """
 3     从类的名字就可以看出,这个将版本信息放在了请求投中
 4     下面是列子的样式
 5     GET /something/ HTTP/1.1
 6     Host: example.com
 7     Accept: application/json; version=1.0
 8     """
 9     invalid_version_message = _('Invalid version in "Accept" header.')
10 
11     def determine_version(self, request, *args, **kwargs):
12         media_type = _MediaType(request.accepted_media_type)
13         # 下面调用的函数就是去请求头中获取数据
14         # self.version_param 头文件中发送过来的版本信息的key
15         # self.default_version 如果头文件中不存在版本信息,默认值
16         version = media_type.params.get(self.version_param, self.default_version)
17         version = unicode_http_header(version)
18         # 调用is_allowed_version方法,判断版本是否合法
19         if not self.is_allowed_version(version):
20             raise exceptions.NotAcceptable(self.invalid_version_message)
21         return version
AcceptHeaderVersioning说明

其它的类都是大同小异,这里就不全部举例了,从上面的结果很容易看出,如果我们需要使用版本获取的配置的话,只需要在配置中定义上使用哪一个方式后定义好一些必要的参数,然后按照上面的示例就可以完成版本的配置和检验

 

 用户验证

由前面可以知道,我们的流程是先进行封装Request后在执行的用户验证这个方法,其实在封装Request的时候,已经为Reqeust对象封装了一个  self.authenticators 的属性,这个属性我们等会儿会用到,封装详情如下:

initialize_request 调用 get_authenticators ,将它的返回值传入到Request对象中

 1  def initialize_request(self, request, *args, **kwargs):
 2     """
 3     Returns the initial request object.
 4     """
 5     parser_context = self.get_parser_context(request)
 6 
 7     return Request(
 8         request,
 9         parsers=self.get_parsers(),
10         # 调用self.get_authenticators(),
11         # 在该方法中遍历配置中的存放对象路径的列表
12         # 分别实例化后的列表作为参数传给Request对象
13         authenticators=self.get_authenticators(),
14         negotiator=self.get_content_negotiator(),
15         parser_context=parser_context
16     )
initialize_request简单说明
1 def get_authenticators(self):
2     """
3     Instantiates and returns the list of authenticators that this view can use.
4     """
5     return [auth() for auth in self.authentication_classes]
get_authenticators代码
1 authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
authentication_classes 配置说明
1 def __init__(self, request, parsers=None, authenticators=None,
2                  negotiator=None, parser_context=None):
3     self._request = request
4     self.authenticators = authenticators or ()  # 存入用户验证相关信息格式 [用来验证的obj, 用来验证的obj]
request的init方法,截取部分

 

现在正式进入用户验证方面,在  initial  函数中,我们调用了 self.perform_authentication(request) 方法来帮助我们完成用户验证

1 def initial(self, request, *args, **kwargs):
2     version, scheme = self.determine_version(request, *args, **kwargs)
3     request.version, request.versioning_scheme = version, scheme
4 
5     # 进行用户验证
6     self.perform_authentication(request)
7     self.check_permissions(request)
8     self.check_throttles(request)
部分initial代码

在 perform_authentication 这个方法中的代码特别简单,就一句 request.user

def perform_authentication(self, request):
    request.user

很容易发现,在这个方法中调用了request中的user方法(使用了装饰器 property)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2017-11-21 18:05  何必从头  阅读(345)  评论(0)    收藏  举报

导航