FastAPI swagger не любит список строк, передаваемых через параметр запроса, но конечная точка работает в браузере

У меня проблема с конечной точкой REST API в FastAPI, которая принимает список строк через один параметр запроса. Пример использования этой конечной точки:

http://127.0.0.1:8000/items/2?short=false&response=this&response=that

Здесь параметр с именем «ответ» принимает список строк, как описано в руководстве по FastAPI, раздел Параметры запроса и проверка строки. Конечная точка работает в браузере должным образом.

FastAPI swagger не любит список строк, передаваемых через параметр запроса, но конечная точка работает в браузере

Однако это не работает в документах Swagger. Кнопка с надписью «Добавить строковый элемент» дрожит при нажатии «Выполнить», чтобы проверить конечную точку. Пользовательский интерфейс Swagger, похоже, не может создать ожидаемый URL-адрес со встроенными параметрами запроса (как показано на рис. 1).

FastAPI swagger не любит список строк, передаваемых через параметр запроса, но конечная точка работает в браузере

Код для конечной точки выглядит следующим образом. Пробовал с проверкой и без.

@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"]}

Благодарен за любую помощь.

1) Если навести курсор на кнопку «Добавить строковый элемент», когда она красная, что скажет всплывающая подсказка? 2) Можете ли вы экспортировать определение OpenAPI из пользовательского интерфейса Swagger и опубликовать часть с параметром response? Я подозреваю, что аннотации кода FastAPI немного неверны.

Helen 18.03.2022 21:11

Возможно связано: github.com/tiangolo/fastapi/issues/4345. См. также github.com/tiangolo/fastapi/issues/1021#issuecomment-5903531‌​81

Helen 18.03.2022 21:13

@Helen привет! Когда я навожу курсор на кнопку, всплывающая подсказка говорит: «Значение должно соответствовать шаблону..». Но сопоставление с образцом работает, как и ожидалось, когда я вызываю конечную точку через браузер.

Sun Bee 18.03.2022 22:54
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
122
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как описано в здесь, это происходит из-за того, что OpenAPI применяет pattern (а также ограничения minimum и maximum) к схеме самого array, а не только к отдельным items в массиве. Если вы нажмете проверил схему OpenAPI на http://127.0.0.1:8000/openapi.json, вы увидите, что схема для параметра response выглядит так, как показано ниже (т. е. проверки применяются и к самому array):

      {
        "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"
      }

Решение 1

Как упоминалось здесь, вместо этого вы можете использовать Pydantic constr, чтобы указать 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...")

Решение 2

Оставьте параметр response как есть. Скопируйте схему OpenAPI из http://127.0.0.1:8000/openapi.json, удалите pattern (а также атрибуты minimum и maximum) из схемы response (array) и сохраните схему OpenAPI в новый файл (например, my_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"
    },
    ...

Затем в своем приложении укажите FastAPI использовать эту схему:

import json
app.openapi_schema = json.load(open("my_openapi.json"))

Решение 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

Во всех приведенных выше решениях аннотация ограничений, которая обычно отображается в OpenAPI под response (т. похоже, это не способ сохранить это. Однако в решениях 2 и 3 вы можете изменить атрибут (query) maxLength: 99 minLength: 3 pattern: ^[a-zA-Z]+$, показанный во фрагменте кода array выше, чтобы добавить аннотацию вручную. Но, поскольку элементы items и т. д. контролируются Swagger, вся аннотация будет отображаться внутри круглых скобок и без разрывов строк между ограничениями. Тем не менее, вы все равно можете информировать пользователей об ограничениях, применяемых к "in", указав их в JSON вашего параметра HTML.

Другие вопросы по теме