立即创建具有自动类型转换、JSON RPC 和 Swagger UI 的 HTTP API。只需添加方法!
项目描述
即时 API
立即创建具有自动类型转换、JSON RPC 和 Swagger UI 的 HTTP API。所有无聊的事情都为您完成,因此您可以专注于有趣的逻辑,同时拥有一个很棒的 API。只需添加方法!
安装
pip install instant-api
或者也安装相应的 Python 客户端:
pip install 'instant-api[client]'
基本用法
只需编写一些 Python 函数或方法并装饰它们即可。参数和返回值需要类型注释,以便您可以将它们转换为 JSON 或从 JSON 转换。您可以将数据类用于复杂值。
from dataclasses import dataclass
from flask import Flask
from instant_api import InstantAPI
app = Flask(__name__)
@dataclass
class Point:
x: int
y: int
@InstantAPI(app)
class Methods:
def translate(self, p: Point, dx: int, dy: int) -> Point:
"""Move a point by dx and dy."""
return Point(p.x + dx, p.y + dy)
def scale(self, p: Point, factor: int) -> Point:
"""Scale a point away from the origin by factor."""
return Point(p.x * factor, p.y * factor)
if __name__ == '__main__':
app.run()
访问http://127.0.0.1:5000/apidocs/获取完整的 Swagger GUI,以交互方式试用 API:
与 API 交谈instant_client
如果您需要 Python 客户端,我强烈推荐配套库Instant_client。它在客户端处理数据转换,并与开发人员工具配合得很好。基本用法如下:
from server import Methods, Point # the classes we defined above
from instant_client import InstantClient
# The type hint is a lie, but your linter/IDE doesn't know that!
methods: Methods = InstantClient("http://127.0.0.1:5000/api/", Methods()).methods
assert methods.scale(Point(1, 2), factor=3) == Point(3, 6)
这看起来很像Methods.scale()
直接调用它,这是重点(不是双关语),但实际上它确实向服务器发送了一个 HTTP 请求。
使用方法路径而不是 JSON-RPC
API 自动提供两种风格,客户可以选择他们喜欢的通信方式:
- 中央 JSON-RPC 端点,完全遵循 JSON-RPC 协议规范,最容易与标准客户端库一起使用。
- 方法路径,这使得人类手动编写请求(尤其是在 Swagger GUI 中)更容易一些,并且更多地使用 HTTP 的特性。
要向方法路径发出请求,请在 URL 末尾包含方法名称,并在 JSON 正文中发送参数对象。这是这样一个调用的样子:
import requests
response = requests.post(
'http://127.0.0.1:5000/api/scale',
json={
'p': {'x': 1, 'y': 2},
'factor': 3,
},
)
assert response.json()['result'] == {'x': 3, 'y': 6}
响应将是一个完整的 JSON-RPC 响应,就像您发出了一个完整的 JSON-RPC 请求一样。特别是,它将有一个result
或一个error
键。
HTTP 状态码
中央 JSON-RPC 端点将始终(除非请求未经过身份验证,请参见下文)返回代码 HTTP 状态代码 200(OK),即使出现错误,正如标准客户端所期望的那样。
由于方法路径不完全是 JSON-RPC,因此它们可能会在出现错误时返回不同的代码。特别是一个无效的请求将导致 400,而方法内部未处理的错误将导致 500。
如果你在方法内部提出一个InstantError
,你可以给它一个http_code
,例如raise InstantError(..., http_code=404)
。仅当方法由方法路径而不是 JSON-RPC 端点调用时,这将成为 HTTP 状态代码。
全局 API 配置
该类InstantAPI
需要一个 Flask 应用程序并具有以下可选的仅关键字参数:
path
是一个字符串(默认值'/api/'
),它将为 JSON RPC 添加到应用程序的端点。每个方法也会有一个基于函数名称的路径,例如/api/scale
和/api/translate
- 请参阅使用方法路径而不是 JSON-RPC。指定不同的字符串以更改所有这些路径。swagger_kwargs
是要传递给flasgger.Swagger
应用程序调用的构造函数的关键字参数的字典(默认为空)。例如,您可以通过将字典传递给config
来自定义 Swagger UI :
api = InstantAPI(app, swagger_kwargs={"config": {"specs_route": "/my_apidocs/", ...}})
处理错误
当服务器遇到错误时,响应将包含一个error
键(而不是一个result
)和一个包含code
、data
和的对象message
。例如,如果为方法提供了无效参数,则错误的详细信息(aTypeError
或 marshmallow ValidationError
)将包含在响应中。错误代码将是-32602
。响应 JSON 如下所示:
{
"error": {
"code": -32602,
"data": {
"p": {
"y": [
"Not a valid integer."
]
}
},
"message": "marshmallow.exceptions.ValidationError: {'p': {'y': ['Not a valid integer.']}}"
},
"id": 0,
"jsonrpc": "2.0"
}
您可以在JSON-RPC 协议规范中找到更多详细信息,包括一些典型错误的标准错误代码。
要返回您自己的自定义错误信息,InstantError
请在您的方法中引发一个,例如:
from instant_api import InstantAPI, InstantError
@InstantAPI(app)
class Methods:
def find_thing(self, thing_id: int) -> Thing:
...
raise InstantError(
code=123,
message="Thing not found anywhere at all",
data=["not here", "or here"],
)
响应将是:
{
"error": {
"code": 123,
"data": [
"not here",
"or here"
],
"message": "Thing not found anywhere at all"
},
"id": 0,
"jsonrpc": "2.0"
}
HTTP 状态代码取决于您使用的 API 的风格 - 请参阅本节。
附加方法
可以使用函数、类或任意对象调用实例,InstantAPI
以向 API 添加方法。对于函数和类,实例可以作为装饰器来调用它。
正如您所期望的,装饰单个函数将其添加为 API 方法。函数本身不应该是类的方法,因为没有办法提供第一个参数self
。
使用对象调用InstantAPI
将搜索其所有属性并将名称不以下划线 ( _
) 开头的所有函数(包括绑定方法)添加到 API。
装饰一个类将构造一个不带参数的类的实例,然后如上所述调用生成的对象。这意味着它将添加绑定方法,因此self
忽略该参数。
所以给定api = InstantAPI(app)
,所有这些都是等价的:
@api
def foo(bar: Bar) -> Spam:
...
api(foo)
@api
class Methods:
def foo(self, bar: Bar) -> Spam:
...
api(Methods)
api(Methods())
如果函数缺少任何参数或返回值的类型注释,则会引发异常。如果您不希望将方法添加到 API,请在其名称前加上下划线,例如def _foo(...)
.
在 Swagger UI 中自定义方法路径
直接设置属性
对于每种方法,flasgger.SwaggerView
都会创建一个。swagger_view_attrs
您可以通过在装饰器的参数中传递类属性字典来自定义视图。例如:
@api(swagger_view_attrs={"tags": ["Stuff"]})
def foo(...)
这将放入Swagger UIfoo
的Stuff
部分。
请注意,以下是Python 3.9 之前的无效语法:
@InstantAPI(app)(swagger_view_attrs={"tags": ["Stuff"]})
def foo(...)
通过文档字符串设置摘要和描述
如果一个方法有一个文档字符串,它的第一行将是summary
方法路径的 OpenAPI 规范中的,在 Swagger UI 的概述中可见。description
当路径在 UI 中展开时,剩余的行将变为可见。
自定义全局请求和方法处理
要直接控制如何处理请求,请创建一个子类InstantAPI
并覆盖以下方法之一:
handle_request(self, method)
是将原始烧瓶请求转换为响应的入口点。如果method
为 None,则向通用 JSON-RPC 路径发出请求。否则method
是请求路径末尾带有方法名称的字符串。call_method(self, func, *args, **kwargs)
使用给定的参数调用 API 方法func
。这里的参数还没有根据函数类型注释反序列化。
super()
除非您正在做一些非常奇怪的事情,否则请记住在某处调用父方法。
验证
要求对请求进行身份验证:
- 创建 的子类
InstantAPI
。 - 覆盖方法
def is_authenticated(self):
。 - 返回一个布尔值:
True
如果用户应该有权访问(基于全局 Flaskrequest
对象),False
是否应该被拒绝。 - 使用子类的实例来装饰方法。
未经身份验证的请求将收到带有非 JSON 正文的 403 响应。
依赖项
datafunctions
(它反过来使用marshmallow
)被两者使用instant_api
并instant_client
透明地处理两端的 JSON 和 Python 类之间的转换。- Flasgger提供了 Swagger UI。
json-rpc
处理协议。
因为其他库做了很多工作,所以instant_api
它本身就是一个非常小的库,基本上包含在一个小文件中。您可能可以很容易地阅读源代码并根据您的需要进行调整。
为什么要使用这个库?
这个库从FastAPI中获得了明显的灵感。那么我为什么要写这个,你为什么要使用它呢?
-
真的很棒
instant_client
,它让您感觉就像在本地调用方法(并且您的 IDE 会像您一样帮助您),即使它们是远程执行的。 -
它更容易设置,因为您不必指定路径或 HTTP 方法。如果您将所有内容分组到一个类中,则只需对整个内容进行一次装饰即可。这几乎是尽可能少的样板代码。
-
JSON-RPC 非常酷。
- 它是一种流行的标准协议,具有以多种语言编写的客户端库。
- 它可以让您进行批量请求:发送一组请求,返回一组响应。
- 当您不关心结果时,它支持通知。
-
当您想使用 Flask(例如,使用其他 Flask 库),或者更一般地说,如果您想要一个 WSGI 应用程序而不必将其嵌入到 FastAPI 中,这非常棒。
当我的用例出现时,我考虑过 FastAPI,但能够使用 Flask(特别是 Plotly Dash)是一项硬性要求。API 只是一个较大项目的一小部分,所以我不希望 FastAPI “负责”。
我尝试查看 FastAPI 的源代码以提取我需要的位,例如从类型注释生成 Swagger 规范,但代码非常复杂,这不值得。所以我编写了我自己的版本,其中依赖项以一种很好的模块化方式完成了类似的艰苦工作。剩下的是一个小的、可读的库,它在很大程度上只是将其他东西连接在一起。这样,如果其他人的情况与我相同,他们的需求略有不同,他们现在可以调整源代码。
项目详情
Instant_api -0.2.0-py3-none-any.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 634856d58b05b22985e92bf0356489b66eae0b446be7bf0b6e608b059ecdd252 |
|
MD5 | 0b51f92c77008544db88a4f65f548e95 |
|
布莱克2-256 | c48b34f5e8500dd547dcec9eef9e95e43acbf147b95d47a9847c5f66b590ffb0 |