1. Tastypie RESTful API请求处理的基本流程描述
Tastypie 是一个基于Python在Django平台上用来创建RESTFul API 框架, 在创建RESTful API 方面提供了强大的功能, 同时在使用上可以很方便地利用Django 自身的Model作为数据源, 也可以很方便地使用非ORM数据源来提供API数据.
除了提供基本的数据功能之外, Tastypie 还提供了 登录验证, 权限控制, 数据验证, 数据缓存, 请求控制, 数据分页, 数据序列与反序列化等功能选项, 来增加API对数据源的各个方面的控制, 以方便我们在数据访问的不同阶段进行控制.
Tastypie 的主要框架架构组成为Api+Resource, Api是一个包含多个Resource集合的对象, Resource通过注册到Api对象中, 然后通过Api对象来进行统一管理. Resource 是一个资源管理实例, 在其中提供真正的资源数据管理, 上述的相关数据源控制的功能特性都是通过选项配置到Resource中来实现的, 也正是这种方式使得功能特性的实现与具体的Resource可以隔离进行处理, 做到逻辑上的解耦.
Tastypie 处理 RESTFul API 基本流程根据请求方式的不同分为三种: 用户读取数据, 用户生成或更新数据, 用户删除数据.
- 用户读取数据:
1. 读取数据列表: get_list
用户通过客户端请求发送查询条件来想服务器请求返回数据的一个流程, 服务器端通过接收请求URL中的查询参数以及Tastypie的Resource的URLConf中的 kwargs 参数 来进行数据过滤, 数据排序, 数据分页处理以及数据的序列化等过程.
1. 接收用户发送请求中的 查询参数集合(包括GET和 URLConf 中的kwargs), 通过 build_filters 来构造数据过滤器
2. 使用 apply_filters 来将这些过滤器应用到原始数据源集合中来过滤符合条件的一部分数据
3. 接收用户的GET请求中的 排序参数(默认为 order_by) 来对2中过滤后的数据进行排序处理: apply_sorting
4. 根据用户的GET请求中的 分页参数(每页数据 limit 和 当前位移 offset)来对3中的数据进行分页处理: Paginator 对象专门处理.
5. 将分页后的数据进行序列化处理: full_dehydrate()->dehydrate_fieldname->dehydrate()
6. 对序列化之后的数据做最后修改: alter_list_data_to_serialize
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2. 读取特定数据对象明细: get_detail
用户通过访问资源详情API来直接获取某个特定资源, 在此过程中会首先访问服务器缓存是否存在该数据来生成或返回数据, 然后对该数据对象做序列化处理, 修改最后序列化后的数据来返回给客户端
1. 查询缓存器中的数据对象: cached_obj_get :Cache 配置选项.
2. 序列化1中返回的数据对象: full_dehydrate->dehydrate_fieldname->dehydrate
3. 最后修改2中序列化后的数据: alter_detail_data_to_serialize
- 1
- 2
- 3
- 4
用户生成或更新数据: post_list / post_detail / put_list / put_detail / patch_list / patch_detail:
创建一个新的资源对象: post_list
1. 用户发送数据的反序列化操作(deserialize), 将用户发送的文本数据转换为Python对象 2. 一个额外的用户接口来修改1中生成的数据对象, 这个是用户层级自定义的: alter_deserialized_detail_data 3. 根据最后的反序列化后的数据生成Bundle对象: build_bundle 4. 根据用户发送数据创建一个新的数据对象: obj_create: ** 在 Resource 中没有实现 ** 5. 根据 Meta中的 always_return_data 来确定是否返回新建数据对象
- 1
- 2
- 3
- 4
- 5
- 6
创建一个资源下的一个新的子集合: post_detail * 在Resource 中没有实现 *
使用一个资源集合来替换另外的一个资源集合: put_list
1. 反序列化用户的请求数据到python对象 2. 删除用户请求对应的资源来准备后续的更新操作: obj_delete_list_for_update: ** 在 Resource 中没有实现 ** 3. 按照用户的请求数据批量地创建新的数据对象: obj_create(此过程是一个事务单元, 出现异常会通过 rollback 来进行事务回滚) 4. 根据 Meta 中的 always_return_data 来确定是否返回新更新的数据集合
- 1
- 2
- 3
- 4
- 5
更新一个已有资源或者创建一个新的资源对象: put_detail
1. 反序列化用户的请求数据: deserialize 2. 用户级别来修改反序列化后的数据对象: alter_deserliazed_detail_data 3. 尝试更新数据: obj_update 4. 更新失败后就创建新数据对象: obj_create 5. 根据 Meta 中的 always_return_data 参数来确定操作成功后是否返回新数据给客户端.
- 1
- 2
- 3
- 4
- 5
- 6
直接替换一个资源集合: patch_list
1. 将请求中的post动作转换为 patch动作: convert_post_to_patch 2. 对用户请求数据进行反序列化操作: deserialize 3. 根据反序列化中各个数据对象的 resource_uri 字段值以及 get_via_uri 来查看是否能够逆向得到Resource实例 4. 如果能够逆向获取Resource实例数据对象, 则尝试更新数据: update_in_place(), 否则就新创建一个数据对象: obj_create() 5. 检查请求集合中请求删除的数据集合, 如果当前Resource 允许删除请求, 则调用 obj_delete() 函数来删除请求中要删除的数据集合.
- 1
- 2
- 3
- 4
- 5
- 6
直接更新一个资源对象: patch_detail
1. 将用户请求的post动作转换为patch动作: convert_post_to_patch 2. 尝试从缓存中获取资源实例对象 3. 对用户请求数据进行反序列化处理 4. 调用 update_in_place 来直接更新资源对象实例 5. 根据 Meta 中的 always_return_data 来确定是否返回更新后的数据实例对象.
- 1
- 2
- 3
- 4
- 5
- 6
用户通过客户端请求发送数据来在服务器端生成数据的工作流程, 其中包括了用户创建新的数据和更新已有的数据两个方面, 但在本质上都是数据创建的过程.
这个过程中的主要工作流程包括:
- 用户删除数据:
1.删除数据列表: delete_list
- 调用 obj_delete_list() 来删除数据列表: * 在 Resource 中没有实现 *
- 返回 HttpNoContent 对象
2.删除具体数据对象:delete_detail:
- 调用 obj_delete() 来删除数据对象: * 在 Resource 中没有实现*
- 返回 HttpNoContent 或 HttpNotFound
2. Tastypie Resource 的Meta 选项配置说明
2.1 用户认证配置 Authentication
Authentication 是一个用来确认当前登录用户身份来验证用户是否可以访问相应的API, 主要用来回答”这个人是谁?”的问题, 涉及到用户登录凭证的请求(如 APIKey, 或者 用户名/密码).
Authentication 的使用很简单, 只需要将其作为Resource类的Meta的一个选项:
authentication = BasicAuthentication()
- 1
Tastypie 自带了以下的Authentication类可以使用:
- Authentication: 没有任何验证的类, 主要用来只读的API
- BasicAuthentication: 使用 HTTP Basic Auth 来检查一个用户的登录凭证.
- ApiKeyAuthentication: 这个是一个敏感数据验证的备用选项, 可以用来验证一个用户名和机器码. 可以在header 的 Authorization 中赋值或者使用 username/api_key 来作为POST或GET的请求参数.
Tastypie 提供了一个自动创建的 APIKey 对象, 通过以下方式来处理:
from django.db.models import signals
from django.contrib.auth.models import User
from tastypie.models import create_api_key
signals.post_save.connect(create_api_key, sender=User)
- 1
- 2
- 3
- 4
- SessionAuthentication: 这个用户验证主要使用内置的Django Session 来检查用户是否登录,对于用一个站点上的JavaScript来访问API很有用. 需要满足用户登录并且有一个活动的回话, 同时也必须有一个合法的 CSRF 标记token
DigestAuthentication: 这个验证使用 HTTP Digest Auth 来检查一个用户的凭证, username是django.contrib.auth.models.User 的 username, 密码应该是机器生成的api key.
OAuthAuthentication: 处理OAuth验证, 主要用来检查分开服务中的用户凭证, 当前支持 OAuth 1.0a 的服务.
MultiAuthentication: 这个类实际上是包装了任意数量的其他验证器的类, 然后分别尝试每一个验证只到某一个成功.
自定义用户凭证验证器实际上很简单, 只需要重载 Authentication的两个方法(其中一个是可选的, 推荐定制, 在API的Caching 和 Throttle中会用到)
def is_authenticated(self, request, **kwargs):
return True/False
# 可选 但是推荐
def get_identifier(self, request):
return string_value
- 1
- 2
- 3
- 4
- 5
- 6
2.2 用户访问权限控制配置: Authorization
Authorization 是一个用来验证用户可以对资源做什么, 主要回答”用户是否授予了权限来执行该动作”, 其中涉及到以下权限的检查:Create/Read/Update/Delete
使用方法直接在Resource类的Meta中, 通过 authorization 选项来赋值.
Tastypie 自带的权限选项:
- Authorization: 没有任何权限检查
- ReadOnlyAuthorization: 仅允许用户读取数据.
DjangoAuthorization: 最高级的权限验证形式, 通过django.contrib.auth.models.Permission 来检查用户授予的权限.
其权限方法与Django的用户权限之间的对应关系:
create_list/detail => add read/update_list/detail => change delete_list_detail => delete
- 1
- 2
- 3
- 4
Authorization API 说明: 一个 Authorization 兼容的类实现了以下的方法:
- read_list(self, object_list, bundle)
- create_list(self, object_list, bundle)
- update_list(self, object_list, bundle)
- delete_list(self, object_list, bundle)
- read_detail(self, object_list, bundle)
- create_detail(self, object_list, bundle)
- update_detail(self, object_list, bundle)
- delte_detail(self, object_list, bundle)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
上面的所有方法都接收 object_list 和 bundle 两个参数, 其中 object_list 是经过请求过滤以及其他限制条件处理之后得到的数据集合. bundle 是一个用 request 填充的 Bundle 对象, 一般我们会经常用到 bundle.request.user 对象.
Authorization 的返回值说明:
对于list方法, 我们可能会过滤 object_list 并返回用户可以访问的数据. 其返回值可以为一个 empty 列表表示用户不可方位, 也可以通过抛出一个 Unauthorized 异常来替换empty
对于 detail 方法, 访问 object_list 来检查 bundle.obj 并返回True(用户可以访问数据对象)或这抛出一个 Unauthorized异常(表示用户没有权限访问).
定制 Authorization 是一个相对简单的过程, 任何与上述的8个API兼容的类都是可接受的
2.3 数据缓存控制配置: Caching
Tastypie 自带了两个缓存类来让数据缓存的使用更加容易, 但是这些缓存是存储在python对象级别的缓存, 主要用来减少数据库的访问频次. 对于数据负载很大的应用, 建议使用一个专门的缓存代理, 如Varnish, 在大部分场景下比Django的视图更快.
Tastypie 自带缓存类的使用流程说明:
- 在 Django 项目的 settings 中配置相应的缓存设置选项 CACHES
- 在 Tastypie 的 Resource 的 Meta 中配置 cache 选项(NoCache, SimpleCache)
设置缓存时可以使用以下参数来初始化Resource的 cache 实例:
- cache_name: 对应 settings 的 CACHES 中的某个配置键名, 用来选择缓存配置
- timeout: 数据对象的缓存时间
- varies: HTTP Vary 设置, 一个列表数据, 如[‘Accept’,’Cookie’]
自定义缓存类的方式很简单, 可以直接从 NoCache 继承并重载 get & set 方法即可, 其中两个方法的签名如下:
def get(self, key):
return object
def set(self, key, value, timeout=somevalue):
pass
- 1
- 2
- 3
- 4
- 5
2.4 数据验证配置: Validation
数据验证主要用来保证用户提交的数据适合用来存储, 这个可以从简单的类型检查到复杂的不同字段一起比较的检查. 如果数据合法会返回一个空的字典数据,相应的数据处理逻辑正常进行. 否则, 会立即返回一个含有错误消息的字典(键名为字段名称, 键值为错误消息列表) 给用户
Tastypie 自带了一下几个数据验证器:
- Validation: 不做任何数据验证, 所有的数据都被认为是合法的, 这是Resource 基类的默认验证器
- FormValidation: 复杂一点的表单验证器, 接收一个 form_class 作为构造器参数, 参数值为一个Django Form(也可以是ModelForm, 但是不会调用它的 save方法) 类, 然后Tastypie 会使用该Form来验证bundle中的所有数据. * 注意该类不会修改用户提交的数据, 仅仅验证用户提交的数据, 如果需要修改用户提交的数据, 需要使用CleaneddataFormValidation *
- CleaneddataFormValidation: 与FormValidation类相似, 也是使用Django的 Form来处理数据验证, 但是会使用 form.cleaned_data 来替换用户发送数据中的bundle的data, 其使用方法与FromValidation一致.
自定义数据验证器也很简单, 只需要继承 Validation 父类, 然后重载 is_valid() 方法, is_valid() 方法的签名如下:
def is_valid(self, bundle, request=None):
return {} or {with errors}
- 1
- 2
2.5 数据访问频次配置: Throttle
为了避免客户端访问数据过于频繁或者出于业务逻辑的考虑需要限制在特定的时间范围内用户的访问频次, 此时就可以通过Tastypie的 Throttle 配置来实现这个功能.
Throttle 的工作原理说明, 在用户每次请求API时, Throttle 会记录用户的每一次访问, 然后通过查看用户访问频次的设置与当前用户访问次数来判断是否用户在特定的时间内访问超频了, 如果超频访问则限制用户的当前访问, 返回一个 HttpTooManyRequest() 对象.
数据访问频次在Resource中的使用方式是通过在Resource的Meta的 throttle 选项来实例化对应的访问频次管理器实例. 相应的构造器接受以下几个参数:
- throttle_at: 用户在特定时间范围内的最大访问次数
- timeframe: 数据访问频次控制的时间范围, 单位为(秒), 默认为3600秒(1小时)
- expiration: api 访问记录的缓存时间周期, 默认为604800秒(1周)
Tastypie 自带了以下几个访问频次控制类:
- BaseThrottle: 没有任何处理, 仅仅实现了一些通用的逻辑以及api兼容的插件, 对开发有用.
- CacheThrottle: 用缓存来管理访问频次限制, 快速但是会受缓存缺失或缓存重启的影响
- CacheDBThrottle: 使用数据库存储来管理访问频次限制, 对于记录用户的访问和仅内存缓存很有用.
自定义数据访问频次管理器与其他组件类似, 继承自 BaseThrottle 然后重载 should_be_throttled 和 accessed 方法, should_be_throttle 方法用来确定用户的访问是否应该被禁止, accessed方法用来记录API的放分历史, 这两个方法的签名如下:
def should_be_throttle(self, identifier, **kwargs):
return True/False
def accessed(self, identifier, **kwargs):
pass
- 1
- 2
- 3
- 4
- 5
2.6 数据分页配置: Paginator
与 Django 的分页器类似, Tastypie自己也包含了一个分页器对象来限制返回给用户的数据的数量, Tastypie使用了limit和offset两个参数来替换Django分页器的page参数, 这样可以不需要计算分页. 除此之外, 还提供了 数据总数(total_count)以及前一页(previous)与后一页(next)链接数据.
分页器在Resource中的使用方式是在Resource的Meta中的 paginator_class 中指定对应的分页器类即可.
自定义分页器可以直接继承自tastypie的Paginator类, 然后重载其 page 方法, page方法的签名如下:
def page(self):
return output
- 1
- 2
通过自定义分页器, 我们可以定制api返回结果中的数据字段, 例如meta中的数据字段, 同时还可以添加额外的数据数据字段或减少某些额外的字段.
2.7 数据序列化与反序列化配置: Serializer
序列化器主要用在用户提交文本数据与后台python对象之间数据的相互转换过程中, 将用户提交数据转换为后台python对象的过程称为反序列化过程, 将后台python对象数据转换为最终发送给用户的数据过程称为序列化过程.
序列化器主要用来控制数据在不同环境中的展现方式来满足各种不同数据格式的数据需求, Tastypie自带了一个序列化器来处理大部分使用案例的基本需求, 而且也很方便我们通过定制来满足不同的需求.
序列化器在Resource中的使用方式是通过在Resource 的 Meta中的 serializer 指定, 指定序列化器时, 可以传递序列化器接收的序列化格式列表:
- formats
- 1
- 2
默认的序列化器支持以下格式的数据序列化操作:
- json
- jsonp
- xml
- yaml
- plist
默认的序列化器中的方法说明:
- get_mime_for_format: 根据格式名来获取格式的 MIME 类型:[application/json]
- format_datetime: 一个用来控制日期时间格式化的接口: [iso-8601]
- format_date: 用来控制日期格式化的接口:[iso-8601]
- format_time: 用来控制时间格式化的接口:[iso-8601]
- to_simple: 将复杂的Python数据结构降低到序列化格式的原类型
- to_formatname: 格式器序列化方法
- from_formatname: 各时期反序列化方法
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
自定义序列化器根据我们的需求不同可以分为以下两类:
1. 对现有支持格式的序列化进行调整
仅需重载Serializer中的 to_formatname/ from_formatname 方法, 其中这两方法的签名如下:
- 1
- 2
- 3
- 4
def to_formatname(self, data, options=None):
return some_serialized_data
def from_formatname(self, content):
return some_deserialized_data
- 1
- 2
- 3
- 4
- 5
2. 增加对其他格式的序列化支持的序列化器:
1. 重载类属性 formats 来添加其他格式名称
2. 重载雷属性 content_types 来添加其他格式的contenttype
3. 添加对其他格式的 to_formatname / from_formatname 方法来实现具体的序列化逻辑