教程:创建 API 端点¶
本文档的这一部分详细介绍了如何为 CloudKitty 的 v2 API 创建一个端点。v1 API 已冻结,不应添加任何端点。
设置新资源布局¶
在本节中,我们将创建一个 example 端点。在 cloudkitty/api/v2/ 中创建以下文件和子目录
cloudkitty/api/v2/
└── example
├── example.py
└── __init__.py
创建自定义资源¶
每个 v2 API 端点都基于一个 Flask Blueprint 和每个子端点一个 Flask-RESTful 资源。这允许对资源进行逻辑分组。以 /rating/hashmap 路由为例。hashmap 模块的每个资源都应是一个 Flask-RESTful 资源(例如 /rating/hashmap/service、/rating/hashmap/field 等)。
注意
应该区分引用单个资源和多个资源的端点。例如,如果您想要一个允许列出某种类型资源的端点,则应实现以下内容
一个支持
GET、POST和PUTHTTP 方法的MyResource资源,路由为/myresource/<uuid:>。一个支持
GETHTTP 方法的MyResourceList资源,路由为/myresource。包含这些资源的蓝图。
基本资源¶
我们将创建一个 /example/ 端点,用于操作水果。我们将创建一个 Example 资源,支持 GET 和 POST HTTP 方法。首先,我们将在 cloudkitty/api/v2/example/example.py 中创建一个包含 get 和 post 方法的类
from cloudkitty.api.v2 import base
class Example(base.BaseResource):
def get(self):
pass
def post(self):
pass
验证方法参数和输出¶
对我们资源的 GET 请求将简单地返回 {“message”: “这是一个示例端点”}。 add_output_schema 装饰器将 voluptuous 验证添加到方法的输出。这允许设置默认值。
- cloudkitty.api.v2.utils.add_output_schema(schema)[source]
为方法的输出添加 voluptuous 模式验证
示例用法
class Example(base.BaseResource): @api_utils.add_output_schema({ voluptuous.Required( 'message', default='This is an example endpoint', ): validation_utils.get_string_type(), }) def get(self): return {}
- 参数:
schema (dict) – 应用于方法输出的模式
让我们更新我们的 get 方法,以便使用此装饰器
import voluptuous
from cloudkitty.api.v2 import base
from cloudkitty import validation_utils
class Example(base.BaseResource):
@api_utils.add_output_schema({
voluptuous.Required(
'message',
default='This is an example endpoint',
): validation_utils.get_string_type(),
})
def get(self):
return {}
注意
在此片段中,get_string_type 在 python2 中返回 basestring,在 python3 中返回 str。
$ curl 'http://cloudkitty-api:8889/v2/example'
{"message": "This is an example endpoint"}
现在是时候实现 post 方法了。此函数将接收一个参数。为了验证它,我们将使用 add_input_schema 装饰器
- cloudkitty.api.v2.utils.add_input_schema(location, schema)[source]
为方法的输入添加 voluptuous 模式验证
接受可以转换为 voluptuous 模式的字典作为参数,并使用此模式验证参数。“location”参数用于指定参数的位置。请注意,对于查询参数,Flask 返回一个
MultiDict。因此,每个字典键都将包含一个列表。为了方便与唯一的查询参数交互,可以使用 voluptuous 验证器SingleQueryParamfrom cloudkitty.api.v2 import utils as api_utils @api_utils.add_input_schema('query', { voluptuous.Required('fruit'): api_utils.SingleQueryParam(str), }) def put(self, fruit=None): return fruit
要接受查询参数列表,可以使用
MultiQueryParamfrom cloudkitty.api.v2 import utils as api_utils @api_utils.add_input_schema('query', { voluptuous.Required('fruit'): api_utils.MultiQueryParam(str), }) def put(self, fruit=[]): for f in fruit: # Do something with the fruit
- 参数:
location (str) – 参数的位置。必须是 [‘body’, ‘query’] 中的一个
schema (dict) – 应用于方法 kwargs 的模式
输入模式验证的参数作为命名参数传递给装饰的函数。让我们实现 post 方法。我们将使用 Werkzeug 异常作为 HTTP 返回代码。
@api_utils.add_input_schema('body', {
voluptuous.Required('fruit'): validation_utils.get_string_type(),
})
def post(self, fruit=None):
policy.authorize(flask.request.context, 'example:submit_fruit', {})
if not fruit:
raise http_exceptions.BadRequest(
'You must submit a fruit',
)
if fruit not in ['banana', 'strawberry']:
raise http_exceptions.Forbidden(
'You submitted a forbidden fruit',
)
return {
'message': 'Your fruit is a ' + fruit,
}
在这里,fruit 预计可以在请求体中找到
$ curl -X POST -H 'Content-Type: application/json' 'http://cloudkitty-api:8889/v2/example' -d '{"fruit": "banana"}'
{"message": "Your fruit is a banana"}
为了从查询中检索 fruit,该函数应这样装饰
@api_utils.add_input_schema('query', {
voluptuous.Required('fruit'): api_utils.SingleQueryParam(str),
})
def post(self, fruit=None):
请注意,这里使用了一个 SingleQueryParam:由于查询参数可以多次指定(例如 xxx?groupby=a&groupby=b),Flask 将查询参数作为列表提供。 SingleQueryParam 助手检查参数是否只提供一次,并返回它。
- class cloudkitty.api.v2.utils.SingleQueryParam(param_type)[source]
Voluptuous 验证器,允许验证唯一的查询参数。
此验证器检查 URL 查询参数是否只提供一次,验证其类型并直接返回它,而不是返回包含单个元素的列表。
请注意,此验证器在内部使用
voluptuous.Coerce进行类型检查。因此,不应将其与cloudkitty.utils.validation.get_string_type在 python2 中一起使用。- 参数:
param_type – 查询参数的类型
警告
SingleQueryParam 在内部使用 voluptuous.Coerce 进行类型检查。因此,不能将 validation_utils.get_string_type 作为 basestring 无法实例化。
加载驱动程序¶
大多数资源需要加载一些驱动程序(存储、SQL…)。由于这些驱动程序的实例化可能需要一些时间,因此应该只执行一次。
一些驱动程序(例如存储驱动程序)在 BaseResource 中加载,因此对所有资源可用。
需要一些额外驱动程序的资源应实现 reload 函数
class BaseResource(flask_restful.Resource):
@classmethod
def reload(cls):
"""Reloads all required drivers"""
这是一个来自 cloudkitty.api.v2.scope.state.ScopeState 的示例
@classmethod
def reload(cls):
super(ScopeState, cls).reload()
cls._client = messaging.get_client()
cls._storage_state = storage_state.StateManager()
注册资源¶
每个端点都应提供一个接受 Flask 应用作为唯一参数的 init 方法。此方法应调用 do_init
- cloudkitty.api.v2.utils.do_init(app, blueprint_name, resources)[source]
将包含一个或多个资源的蓝图注册到应用。
- 参数:
app (flask.Flask) – 要注册蓝图的 Flask 应用
blueprint_name (str) – 要创建的蓝图的名称
resources (与
cloudkitty.api.v2.RESOURCE_SCHEMA匹配的字典列表) – 要添加到蓝图的 Api 的资源
将以下内容添加到 cloudkitty/api/v2/example/__init__.py
from cloudkitty.api.v2 import utils as api_utils
def init(app):
api_utils.do_init(app, 'example', [
{
'module': __name__ + '.' + 'example',
'resource_class': 'Example',
'url': '',
},
])
return app
在这里,我们使用作为参数传递的 Flask 应用、蓝图名称和资源列表调用 do_init。蓝图名称将作为所有资源的 URL 的前缀。每个资源都由一个具有以下属性的字典表示
module:包含资源类的 python 模块的名称resource_class:资源的类url:URL 后缀
在我们的例子中,Example 资源将在 /example 处提供(蓝图名称 + URL 后缀)。
注意
如果您需要将资源添加到现有端点,只需将其添加到列表中即可。
警告
如果您创建了一个新模块,则必须将其添加到 cloudkitty/api/v2/__init__.py 中的 API_MODULES
API_MODULES = [
'cloudkitty.api.v2.example',
]
记录您的端点¶
v2 API 使用 os_api_ref 进行文档记录。每个 v2 API 端点必须在 doc/source/api-reference/v2/<endpoint_name>/ 中记录。