跳转到主要内容

通过自动类型转换、JSON RPC和Swagger UI立即创建HTTP API。只需添加方法即可!

项目描述

logo

Build Status Coverage Status Supports Python versions 3.7+

instant_api

通过自动类型转换、JSON RPC和Swagger UI立即创建HTTP API。所有无聊的工作都为您完成,让您可以专注于有趣的逻辑,同时拥有一个酷炫的API。只需添加方法即可!

安装

pip install instant-api

或者安装相应的Python客户端

pip install 'instant-api[client]'

基本用法

只需编写一些Python函数或方法并为其添加装饰。参数和返回值需要类型注解,以便它们可以被转换为JSON。您可以使用dataclasses来处理复杂值。

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/以尝试交互式API

Swagger overview

使用instant_client与API通信

如果您需要一个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自动提供两种版本,客户端可以选择他们更喜欢的通信方式

  1. 中央JSON-RPC端点,严格遵循JSON-RPC协议规范,并且与标准客户端库配合使用最为简便。
  2. 方法路径,使人类手动编写请求稍微容易一些(尤其是在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)。这将变成HTTP状态码仅当方法是通过方法路径而不是JSON-RPC端点调用的

全局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/", ...}})

处理错误

当服务器遇到错误时,响应将包含一个包含codedatamessage的对象的error键(而不是result)。例如,如果方法被给定了无效的参数,错误详情(无论是TypeError还是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(...)

这将把 foo 放入Swagger UI的 Stuff 部分。

请注意,以下语法在 Python 3.9之前是无效的

@InstantAPI(app)(swagger_view_attrs={"tags": ["Stuff"]})
def foo(...)

通过文档字符串设置摘要和描述

如果方法有一个文档字符串,它的第一行将是方法路径在OpenAPI规范中的 summary,在Swagger UI的概览中可见。其余的行将成为 description,在UI中展开路径时可见。

自定义全局请求和方法处理

为了直接控制请求的处理方式,创建一个 InstantAPI 的子类并重写以下方法之一

  • handle_request(self, method) 是入口点,它将原始Flask请求转换为响应。如果 method 为None,则请求是针对通用JSON-RPC路径的。否则,method 是一个字符串,包含请求路径末尾的方法名称。
  • call_method(self, func, *args, **kwargs) 使用给定的参数调用API方法 func。这里的参数尚未根据函数的类型注解进行反序列化。

除非你正在做一些非常奇怪的事情,否则请记住在某个地方调用父方法 super()

身份验证

为了要求请求进行身份验证

  1. 创建一个 InstantAPI 的子类。
  2. 重写方法 def is_authenticated(self):
  3. 返回一个布尔值:如果用户应该有权访问(基于全局Flask request 对象),则为 True,如果应该拒绝,则为 False
  4. 使用您的子类的一个实例来装饰方法。

未认证的请求将收到一个带有非JSON正文的403响应。

依赖项

  • datafunctions(它又使用 marshmallow)被 instant_apiinstant_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 (12.4 kB 查看哈希)

上传时间 Python 3

由以下机构支持

AWSAWS云计算和安全赞助商DatadogDatadog监控FastlyFastlyCDNGoogleGoogle下载分析MicrosoftMicrosoftPSF赞助商PingdomPingdom监控SentrySentry错误日志StatusPageStatusPage状态页面