FastAPI swagger 不喜欢通过查询参数传递的字符串列表,但端点在浏览器中工作

Sun*_*Bee 5 swagger-ui fastapi

我在 FastAPI 中的 REST API 端点上遇到问题,该端点通过单个查询参数接受字符串列表。此端点的用法示例如下:

http://127.0.0.1:8000/items/2?short=false&response=this&response=that
Run Code Online (Sandbox Code Playgroud)

此处,名为“response”的参数接受 FastAPI 教程“查询参数和字符串验证”部分中记录的字符串列表。端点在浏览器中按预期工作。

图。1

但是,它在 Swagger 文档中不起作用。单击“执行”以测试端点时,标记为“添加字符串项目”的按钮会晃动。Swagger UI 似乎无法使用嵌入的查询参数创建预期的 URL(如图 1 所示)。

图2

端点的代码如下。我尝试过有验证和没有验证。

@app.get("/items/{item_ID}")
async def getQuestion_byID(item_ID: int = Path(
                    ...,
                    title = "Numeric ID of the question",
                    description = "Specify a number between 1 and 999",
                    ge = 1,
                    le = 999
                ), response: Optional[List[str]] = Query(
                    [],
                    title="Furnish an answer",
                    description="Answer can only have letters of the alphabet and is case-insensitive",
                    min_length=3,
                    max_length=99,
                    regex="^[a-zA-Z]+$"
                ), short: bool = Query(
                    False,
                    title="Set flag for short result",
                    description="Acceptable values are 1, True, true, on, yes"
                )):
    """
    Returns the quiz question or the result.
    Accepts item ID as path parameter and
    optionally response as query parameter.
    Returns result when the response is passed with the item ID. 
    Otherwise, returns the quiz question.
    """
    item = question_bank.get(item_ID, None)
    if not item:
        return {"question": None}
    if response:
        return evaluate_response(item_ID, response, short)
    else:
        return {"question": item["question"]}
Run Code Online (Sandbox Code Playgroud)

感谢您的任何帮助。

Chr*_*ris 6

如此处所述,发生这种情况是因为 OpenAPI 将pattern(以及minimum约束maximum)应用于自身的架构array,而不仅仅是items数组中的个体。如果您在http://127.0.0.1:8000/openapi.json检查 OpenAPI 架构,您会看到参数的架构如下所示(即,验证也应用于其自身):responsearray

      {
        "description": "Answer can only have letters of the alphabet and is case-insensitive",
        "required": false,
        "schema": {
          "title": "Furnish an answer",
          "maxLength": 99,
          "minLength": 3,
          "pattern": "^[a-zA-Z]+$",
          "type": "array",
          "items": {
            "maxLength": 99,
            "minLength": 3,
            "pattern": "^[a-zA-Z]+$",
            "type": "string"
          },
          "description": "Answer can only have letters of the alphabet and is case-insensitive",
          "default": []
        },
        "name": "response",
        "in": "query"
      }
Run Code Online (Sandbox Code Playgroud)

解决方案1

正如这里提到的,您可以使用 Pydanticconstr来指定items该约束:

my_constr = constr(regex="^[a-zA-Z]+$", min_length=3, max_length=99)
response: Optional[List[my_constr]] = Query([], title="Furnish an...", description="Answer can...")
Run Code Online (Sandbox Code Playgroud)

解决方案2

保持您的response参数不变。从http://127.0.0.1:8000/openapi.json复制 OpenAPI 架构,从( ) 架构中删除pattern(以及minimummaximum属性)并将 OpenAPI 架构保存到新文件(例如)。它应该看起来像这样:responsearraymy_openapi.json

    ...
    {
    "description": "Answer can only have letters of the alphabet and is case-insensitive",
    "required": false,
    "schema": {
      "title": "Furnish an answer",
      "type": "array",
      "items": {
        "maxLength": 99,
        "minLength": 3,
        "pattern": "^[a-zA-Z]+$",
        "type": "string"
      },
      "description": "Answer can only have letters of the alphabet and is case-insensitive",
      "default": []
    },
    "name": "response",
    "in": "query"
    },
    ...
Run Code Online (Sandbox Code Playgroud)

然后,在您的应用程序中,指示 FastAPI 使用该架构:

import json
app.openapi_schema = json.load(open("my_openapi.json"))
Run Code Online (Sandbox Code Playgroud)

解决方案3

由于上述解决方案需要您在每次进行更改或添加新端点/参数时复制和编辑架构,因此您宁愿按照此处所述修改 OpenAPI架构。这将使您免于复制/编辑架构文件。确保在代码末尾添加以下内容(定义所有路由后)。

from fastapi.openapi.utils import get_openapi

def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title="FastAPI",
        version="0.1.0",
        description="This is a very custom OpenAPI schema",
        routes=app.routes,
    )
    del openapi_schema["paths"]["/items/{item_ID}"]["get"]["parameters"][1]["schema"]["maxLength"]
    del openapi_schema["paths"]["/items/{item_ID}"]["get"]["parameters"][1]["schema"]["minLength"]
    del openapi_schema["paths"]["/items/{item_ID}"]["get"]["parameters"][1]["schema"]["pattern"]
    
    app.openapi_schema = openapi_schema
    return app.openapi_schema
    
    
app.openapi = custom_openapi
Run Code Online (Sandbox Code Playgroud)

在上述所有解决方案中,通常在 OpenAPI 中显示的约束注释response(即(query) maxLength: 99 minLength: 3 pattern: ^[a-zA-Z]+$)不会出现(因为 Swagger 会根据应用于 的约束创建该注释array,而不是items),但似乎没有成为保存它的一种方式。但是,在解决方案 2 和 3 中,您可以修改"in"属性(如上面的代码片段所示)JSON来手动添加注释。但是,由于HTML元素等由 Swagger 控制,整个注释将出现在括号内,并且约束之间没有换行符。尽管如此,您仍然可以通过在参数的items中指定约束来告知用户适用于 的约束。descriptionQuery