😜Изучите FastAPI с нуля (1) — краткая часть официального исследования по извлечению документов

Python
😜Изучите FastAPI с нуля (1) — краткая часть официального исследования по извлечению документов

предисловие

В прошлом онлайн-бизнес всегда использовал две среды, Bottle и Flask, для написания интерфейсов API. Bottle и Flask — это синхронные фреймворки, а последний из них, поддерживающий асинхронные фреймворки, — очень удачный вариант: ранее вЦиннаньский университетЯ также узнал о его странностях в статье из .Мне было интересно изучить асинхронные фреймворки, поэтому я решил начать использовать Fastapi, чтобы попробовать.Хотя я использовал Tornado раньше, он ограничен использованием синхронных фреймворков.

Я изначально хотел попробовать sanic, но мне было любопытно, поэтому я все же повозился со следующим Fastapi!

Лучший способ выучить новую структуру, конечно, официальная документация, предоставленная рамками глупыми!

ссылка:fastapi.tiangolo.com

Исходный код:GitHub.com/Тион Ох/Пост…

Использованная литература:Woohoo.Краткое описание.com/afraid/94710 квота 35…

начать кодирование

Вопросы, требующие внимания, поскольку FastAPI поддерживает только API Python3.6+, вам нужно учиться и практиковаться в среде Python3.6+!

Это означает, что, поскольку я работаю в среде Windows, он используется только для открытой отладки.Если он действительно находится в сети, он должен работать в среде Linux для достижения максимальной производительности, связанной с ASGI!

1: Установка зависимых библиотек

или:

FastAPI - это современный, быстрый (высокопроизводительный) веб-фреймворк на Python.

pip install fastapi

uvicorn — сервер, в основном используемый для загрузки и обслуживания приложений.

pip install uvicorn

2: Первый привет, мир

import uvicorn
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

if __name__ == '__main__':
    uvicorn.run(app=app)

Углубитесь в метод uvicorn.run(app=app) и увидите один из них:

def run(app, **kwargs):
    config = Config(app, **kwargs)
    server = Server(config=config)

    if (config.reload or config.workers > 1) and not isinstance(app, str):
        logger = logging.getLogger("uvicorn.error")
        logger.warn(
            "You must pass the application as an import string to enable 'reload' or 'workers'."
        )
        sys.exit(1)

    if config.should_reload:
        sock = config.bind_socket()
        supervisor = StatReload(config, target=server.run, sockets=[sock])
        supervisor.run()
    elif config.workers > 1:
        sock = config.bind_socket()
        supervisor = Multiprocess(config, target=server.run, sockets=[sock])
        supervisor.run()
    else:
        server.run()

Углубившись в config = Config(app, **kwargs), вы увидите множество связанных элементов информации о конфигурации:

class Config:
    def __init__(
        self,
        app,
        host="127.0.0.1",
        port=8000,
        uds=None,
        fd=None,
        loop="auto",
        http="auto",
        ws="auto",
        lifespan="auto",
        env_file=None,
        log_config=LOGGING_CONFIG,
        log_level=None,
        access_log=True,
        use_colors=None,
        interface="auto",
        debug=False,
        reload=False,
        reload_dirs=None,
        workers=None,
        proxy_headers=True,
        forwarded_allow_ips=None,
        root_path="",
        limit_concurrency=None,
        limit_max_requests=None,
        backlog=2048,
        timeout_keep_alive=5,
        timeout_notify=30,
        callback_notify=None,
        ssl_keyfile=None,
        ssl_certfile=None,
        ssl_version=SSL_PROTOCOL_VERSION,
        ssl_cert_reqs=ssl.CERT_NONE,
        ssl_ca_certs=None,
        ssl_ciphers="TLSv1",
        headers=None,
    ):

Таким образом, параметры, которые можно добавить, можно заполнить, посмотрев информацию о нескольких параметрах конфигурации выше:

Таким образом, его можно изменить на:

import uvicorn
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello 454533343433World"}

if __name__ == '__main__':
    uvicorn.run(app=app, host="127.0.0.1", port=8000, reload=True, debug=True)

Я обнаружил, что хочу обновить код в горячем виде, но результат? Есть предупреждающее сообщение:

WARNING:  You must pass the application as an import string to enable 'reload' or 'workers'.

Это переводится как: Предупреждение: приложение должно быть передано как строка импорта, чтобы включить «перезагрузку». Тогда что: Я изменил на:

  uvicorn.run(app='app', host="127.0.0.1", port=8000, reload=True, debug=True)

Еще одно напоминание:

ERROR:    Error loading ASGI app. Import string "app" must be in format "<module>:<attribute>".

Ну, я смотрю официальную документацию и говорю, что это:

Это требуется в командной строке: модуль плюс имя приложения: только в приведенном выше сообщении об ошибке также говорится, что:

Ну поймите:

    uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)

После этого можно запустить горячее обновление и перезапустить службу!

Затем получите доступ к адресу и, как правило, получите тело сообщения, возвращаемое интерфейсом:

Затем проверьте адрес взаимодействия с документацией API в соответствии с официальной документацией сайта:

http://127.0.0.1:8000/docs

http://127.0.0.1:8000/redoc

Методы маршрутизации: GET, POST, PUT, PATCH, DELETE и OPTIONS.

@app.post("/")
@app.put("/")
@app.delete("/")
@app.get("/")
@app.options("/")
@app.head("/")
@app.patch("/")
@app.trace("/")
async def root():
    return {"message": "Hello 454533333343433World"}

3: Сбор и проверка параметров на маршруте Маршрут

Вообще у нас роутинг статический и динамический.Статический роут в том,что параметры фиксированы и прописаны,то есть адрес доступа прописан,и нужен динамический адрес,и адрес транскриптора аналогичен адресу сообщение 94710ed35b92 является динамическим, фактически Bottle and Flask.

Woohoo.Краткое описание.com/afraid/94710 квота 35…

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id):
    return {"item_id": item_id}

Item_id в приведенном выше примере кода является динамическим параметром, вы можете передать его по желанию.

Затем идет определение проверки данных для входящих параметров, таких как бутылка: как:

from fastapi import FastAPI

app = FastAPI()



@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

item_id: int В этом случае item_id должен быть данными, которые можно преобразовать в int, иначе будет сообщено об ошибке!

По вопросам покрытия маршрутизации: Следующие два адреса маршрутизации:

from fastapi import FastAPI
app = FastAPI()
@app.get("/users/me")
async def read_user_me():
    return {"user_id": "the current user"}


@app.get("/users/{user_id}")
async def read_user(user_id: str):
    return {"被优先匹配到:": user_id}

Если два вышеупомянутых маршрута существуют одновременно, /users/{user_id} перезапишет /users/me!

3.1 Параметры пути запроса и проверка параметров

Что касается параметров запроса, на самом деле это информация о параметрах при отправке с использованием POSTMAN: как:

http://127.0.0.1:8000/items/?skip=0&limit=10

skip=0&limit — так называемый параметр запроса.

from fastapi import FastAPI

app = FastAPI()

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
    return fake_items_db[skip : skip + limit]

Первый случай доступа:

Вторая ситуация доступа:

Третья ситуация доступа:

3.2 Многолучевое распространение и параметры запроса

Так называемые параметры Multi-Path и Query являются динамическими параметрами на URL-адреса, а параметры, которые необходимо представить через и сепаратор. Эта ситуация обычно очень распространена в получении представлений, так как с этим бороться?


from fastapi import FastAPI

app = FastAPI()


@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(
    user_id: int, item_id: str, q: str = None, short: bool = False
):
    item = {"item_id": item_id, "owner_id": user_id}
    if q:
        item.update({"q": q})
    if not short:
        item.update(
            {"description": "This is an amazing item that has a long description"}
        )
    return item

просить:

http://127.0.0.1:8000/users/123456/items/items_xinxiid/?q=assa&short=True

просить:

http://127.0.0.1:8000/users/123456/items/items_xinxiid/?q=assa&short=False

3.3 Обязательные и необязательные параметры пути и параметры запроса

Необязательные и обязательные параметры в основном определяются тем, следует ли задавать значения по умолчанию:

как:

@app.get("/items/{item_id}")
async def read_user_item(item_id: str, needy: str):
    item = {"item_id": item_id, "needy": needy}
    return item

В приведенном выше коде для needy не задано значение по умолчанию. Если это значение не будет отправлено, появится сообщение об ошибке:

Вы также можете определить типы отправки для необязательных и обязательных параметров: Вы также можете использовать Optional для определения типа данных, которые необходимо отправить: как:

from typing import Optional

@app.get("/items/{item_id}")
async def read_user_item(item_id: str, limit: Optional[int] = None):
    item = {"item_id": item_id, "limit": limit}
    return item

Мы указываем, что предел параметра запроса имеет тип int, но это необязательный параметр, для которого установлено значение None:

3.4 Перечисление параметров пути

import uvicorn
from fastapi import FastAPI
from enum import Enum


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/model/{model_name}")
async def get_model(model_name: ModelName):
    if model_name == ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}
    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}
    return {"model_name": model_name, "message": "Have some residuals"}


if __name__ == '__main__':
    uvicorn.run(app='main2:app', host="127.0.0.1", port=8000, reload=True, debug=True)

Посетив адрес:

http://127.0.0.1:8000/model/alexnet

3.5 Другие проверки параметров запроса

В прошлом было принято использовать wtform для определения похожих, необязательных или длинных типов представленной информации поля. В Fastapi мы передаем: из fastapi импортировать FastAPI, Query в Query для определения, например:

from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(None, min_length=3,max_length=50),regex="^fixedquery$"):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

q: q: str = Query(None, min_length=3,max_length=50),regex="^fixedquery$") Это означает: параметр q является необязательным параметром, но если он заполнен, максимальная длина должна быть меньше 50, а минимальная длина должна быть больше 3: и он должен соответствовать регулярному выражению

Конечно, None можно изменить на другие значения по умолчанию, которые можно записать как:

q: q: str = Query('xiaozhong', min_length=3,max_length=50),regex="^fixedquery$")

Без прохождения q:

http://127.0.0.1:8000/items/

В случае передачи q и длины больше 50:

http://127.0.0.1:8000/items/

В случае прохождения q и длины меньше 3:

http://127.0.0.1:8000/items/?q=4

Параметр запроса Регулярная проверка параметра запроса

3.6 Параметр запроса Список параметров запроса с несколькими значениями

Как правило, в нашем интерфейсе редко говорится, что один и тот же параметр передает несколько значений, например:

http://localhost:8000/items/?q=foo&q=bar

Но расследования этой ситуации нет, поэтому мы также можем определить, что подобные параметры должны быть в виде списка:

from typing import List

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: List[str] = Query(["foo", "bar"])):
    # <!--也可以使用list直接代替List[str]:-->
    query_items = {"q": q}
    return query_items

По умолчанию:

Значение не по умолчанию:

3.7 Другие методы проверки параметров пути

Для параметров запроса вы можете использовать Query, а для параметров пути вы также можете использовать Path, который поставляется с Fastapi для проверки.

from fastapi import FastAPI, Path

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    q: str, item_id: int = Path(..., title="The ID of the item to get")
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

Для проверки параметра пути также можно проверить, что item_id больше или равно, например:


from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(
 *, item_id: int = Path(..., title="The ID of the item to get", ge=1), q: str ):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

В приведенном выше коде означает, что когда ge = 1, item_id должен быть целым числом «g больше или равно e, равному 1».

3.8 Тело запроса, представленное параметрами

Как правило, тело запроса не передается через get, а параметры, передаваемые с помощью get, обычно называются параметрами запроса. Поэтому, если информация о параметрах отправляется через POTS, PUT и т. д., мы обычно помещаем ее в тело запроса, чтобы отправить ее нашему бэкенду.

Для получения и проверки тела запроса используется форма, предоставляемая FastApi: from pydantic import BaseModel

Пример выглядит следующим образом:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item

В приведенной выше модели я определяю, в каком формате должен быть отправленный элемент, например, имя является обязательным полем, описание является необязательным и по умолчанию равно «Нет», цена является обязательной и должна иметь плавающий тип, налог не является обязательным и по умолчанию равен «Нет».

Как клиент передает вышеуказанные параметры?

Попробуйте отправить параметры, ничего не написав:

В случае отправки параметров в формате JSON:

Преднамеренная отправка запроса неправильного формата параметра:

3.8 Тело запроса и сочетание запроса и пути

В процессе разработки некоторых API-интерфейсов неизбежно, что некоторые из вышеупомянутых комбинаций гибридных приложений необходимо использовать всесторонне, а также одновременно отправлять и получать несколько параметров.

Так как же мы обычно получаем этот параметр, когда мы его обычно получаем?

Пример кода вроде:

from fastapi import FastAPI, Path
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),
    q: str = None,
    item: Item = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    if item:
        results.update({"item": item})
    return results

Через предыдущее исследование на самом деле очень просто, и правда все еще такая же, как в примере запроса выше:

3.9 Представление нескольких запросов

Для более сложных предприятий фактически будет несколько представлений Boay.В предыдущем заказе торгового центра клиент мог одновременно отправлять объектную информацию о нескольких объектах на серверную часть, например объект заказа, объект адреса и объект информации о товаре. Ждать.

Итак, как принять несколько объектов Body в Fastapi? Обычно в бутылке информация, представленная на стороне клиента, может быть получена напрямую через request.body или request.json.

В Fastapi предполагается, что параметры, представленные клиентом, имеют вид:

{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    },
    "user": {
        "username": "dave",
        "full_name": "Dave Grohl"
    }
}

А как насчет получения?

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None


class User(BaseModel):
    username: str
    full_name: str = None


@app.put("/items/{item_id}")
async def update_item(*, item_id: int, item: Item, user: User):
    results = {"item_id": item_id, "item": item, "user": user}
    return results

В этом случае клиент отправляет несколько объектов сущности. Это может определить несколько объектов модели. fastapi автоматически обрабатывает информацию для извлечения.

Если дополнительные предположения:

В Fastapi предполагается, что параметры, представленные клиентом, имеют вид:

{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    },
    "user": {
        "username": "dave",
        "full_name": "Dave Grohl"
    },
    "importance": 5
}

На самом деле такой возможности не существует, так как же прочитать и проанализировать параметр важности? Так как в параметрах есть Query и Path, конечно же будет и Body.

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None


class User(BaseModel):
    username: str
    full_name: str = None


@app.put("/items/{item_id}")
async def update_item(
    *, item_id: int, item: Item, user: User, importance: int = Body(...,gt=0)
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    return results

В приведенном выше коде мы вводим Body, обрабатываем и извлекаем его по важности: int = Body(...):

Если далее предполагается, что клиент отправляет один встроенный объект, что нам нужно сделать? :

{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    }
}

FastAPI предоставляет один:

item: Item = Body(..., embed=True) детали следующим образом:

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None


@app.put("/items/{item_id}")
async def update_item(*, item_id: int, item: Item = Body(..., embed=True)):
    results = {"item_id": item_id, "item": item}
    return results

Пример запроса:

Что, если вдобавок клиент представит более сложную вложенную модель? Это определенно относится к конопляным яйцам! Есть списки и сущности во вложенности.

как:

{
    "name": "Foo",
    "description": "The pretender",
    "price": 42.0,
    "tax": 3.2,
    "tags": ["rock", "metal", "bar"],
    "image": {
        "url": "http://example.com/baz.jpg",
        "name": "The Foo live"
    }
}

На данный момент нам нужно так называемое подвложение:

from typing import Set

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Image(BaseModel):
    url: str
    name: str


class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None
    tags: Set[str] = []
    image: Image = None


@app.put("/items/{item_id}")
async def update_item(*, item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

Как показано в приведенном выше коде, элемент содержит изображение, а также содержит определение списка типа тегов.

Более глубокая вложенность MMP также может быть определена как:

{
    "name":"Foo",
    "description":"The pretender",
    "price":42,
    "items":[
        {
            "name":"Foo",
            "description":"The pretender",
            "price":42,
            "tax":3.2,
            "tags":[
                "rock",
                "metal",
                "bar"
            ],
            "image":{
                "url":"http://example.com/baz.jpg",
                "name":"The Foo live"
            }
        },
        {
            "name":"Foo2",
            "description":"The 2",
            "price":422,
            "tax":3.2,
            "tags":[
                "rock",
                "metal",
                "bar"
            ],
            "image":{
                "url":"http://example.com/baz.jpg",
                "name":"The Foo live"
            }
        }
    ]
}

Соответствующий анализ:

from typing import Set
from typing import List, Set

class Image(BaseModel):
    url: str
    name: str
class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None
    tags: Set[str] = []
    # images: List[Image] = None
    image: Image = None


class Offer(BaseModel):
    name: str
    description: str = None
    price: float
    items: List[Item]


@app.post("/offers/")
async def create_offer(*, offer: Offer):
    return offer

3.10 Поле тела запроса

Значение поля Поле на самом деле аналогично приведенному выше Запросу, Пути, а также добавляет соответствующую проверку к информации полей в Теле.

То есть. Используйте Field для стандартизации представленной информации о параметре Body.

как:

from fastapi import Body, FastAPI
from pydantic import BaseModel, Field

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str = Field(None, title="标题啊",description="错误提示文字啊", max_length=300)
    price: float = Field(..., gt=0, description="错误提示文字啊")
    tax: float = None


@app.put("/items/{item_id}")
async def update_item(*, item_id: int, item: Item = Body(..., embed=True)):
    results = {"item_id": item_id, "item": item}
    return results

Вышеупомянутое означает, что это то же самое, что и ранее определенная проверка параметра.

нормальная ситуация:

нештатная ситуация:

3.11 Проверка других типов данных

Для проверки формата данных обычно мы не ограничиваются

  • int
  • float
  • str
  • bool

Тем не менее, параметры подачи больше, чем в вышеуказанном формате, иногда, например, проверка номера мобильного телефона, иногда проверка типа времени и т. д.

Другие типы:

Другие типы данных¶ Вот некоторые другие типы данных, которые вы можете использовать (из официальной документации):

  • UUID:
    • Стандартный «универсальный уникальный идентификатор», общий для идентификаторов во многих базах данных и системах.
    • В запросах и ответах будет представлена ​​как str.
  • datetime.datetime:
    • Pythondatetime.datetime.
    • В запросе и ответе он будет представлен как STR для использования формата ISO 8601, например: 2008-09-15T15:53:00+05:00.
  • datetime.date:
    • Pythondatetime.date.
    • В запросах и ответах он будет представлен как str в формате ISO 8601, например: 2008-09-15.
  • datetime.time:
    • Pythondatetime.time.
    • В запросах и ответах он будет представлен как str в формате ISO 8601, например: 14:23:55.003.
  • datetime.timedelta:
    • pythondatetime.timedelta.
    • В запросах и ответах общее количество секунд будет представлено в виде числа с плавающей запятой.
    • Pydantic также позволяет представить это как «Кодирование разницы во времени ISO 8601», см. документацию для получения дополнительной информации. .
  • frozenset:
    • По запросам и ответам, подумайте об этом как набор:
    • В запросе список будет прочитан, дедуплицирован и преобразован в набор.
    • В ответ набор будет преобразован в список.
    • В сгенерированной схеме будет указано, что заданные значения уникальны (с использованием уникальных элементов JSONSchema).
  • bytes:
    • Стандартные байты Python.
    • Будет рассматриваться как str в запросах и ответах.
    • В сгенерированной схеме будет указано, что это str с двоичным «форматом».
  • Decimal:
    • Стандартный пифитдесимальный.
    • В запросах и ответах обработка такая же, как и у float.

Поэтому я также могу использовать другие типы для проверки:

from datetime import datetime, time, timedelta
from uuid import UUID

from fastapi import Body, FastAPI

app = FastAPI()


@app.put("/items/{item_id}")
async def read_items(
    item_id: UUID,
    start_datetime: datetime = Body(None),
    end_datetime: datetime = Body(None),
    repeat_at: time = Body(None),
    process_after: timedelta = Body(None),
):
    start_process = start_datetime + process_after
    duration = end_datetime - start_process
    return {
        "item_id": item_id,
        "start_datetime": start_datetime,
        "end_datetime": end_datetime,
        "repeat_at": repeat_at,
        "process_after": process_after,
        "start_process": start_process,
        "duration": duration,
    }

4: ответное сообщение

4.1 Определение с помощью response_model

При запросе интерфейса наш клиент может видеть так называемое ответное сообщение, такое как заголовок ответа, код ответа, содержимое ответа и т. д.

Обычно не так глупо возвращать то, что вводит пользователь. Следующий пример официального веб-сайта предназначен исключительно для демонстрации:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()



class UserIn(BaseModel):
    username: str
    password: str
    email: str
    full_name: str = None


class UserOut(BaseModel):
    username: str
    email: str
    full_name: str = None


@app.post("/user/", response_model=UserOut)
async def create_user(*, user: UserIn):
    return user

После запроса получается информация о содержимом UserOut:

Обычно, когда мы определяем наш API для возврата ответа, он обычно возвращает фиксированный формат JSON, поэтому мы можем напрямую использовать определение response_model в качестве словаря:

from typing import Dict

from fastapi import FastAPI

app = FastAPI()


@app.get("/keyword-weights/", response_model=Dict[str, float])
async def read_keyword_weights():
    return {"foo": 2.3, "bar": 3.4}

4.2 О коде статуса ответа status_code

Обычно запрос интерфейса завершается, и если исключения нет, обычно возвращается 200: Как журнал распечатывает:

INFO:     127.0.0.1:58141 - "POST /user/ HTTP/1.1" 400
INFO:     127.0.0.1:58315 - "POST /user/ HTTP/1.1" 200

FastAPI запускает указанный нами возвращаемый код состояния

Следующий пример:

@app.post("/user/", response_model=UserOut,status_code=500)
async def create_user(*, user: UserIn):
    return user

Заставляет запрашиваемый интерфейс возвращать:

Вы даже можете быть указаны в состоянии импорта:

from fastapi import FastAPI, status

app = FastAPI()


@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(name: str):
    return {"name": name}
    

5: Обработка ошибок

5.1 Выброшено исключение HTTPException

До этого на самом деле был класс исключения HttpError в Bottle, и в FastAPI тоже было такое HTTPException.

Например:

from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}


@app.get("/items/{item_id}")
async def read_item(item_id: str):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item": items[item_id]}

В приведенном выше коде активно выдается ошибка 404, определяющая, существует ли item_id в элементах.

Мы просмотрели исходный код HTTPException и StarletteHTTPException и обнаружили, что они также унаследованы от Exception:

class HTTPException(StarletteHTTPException):
    def __init__(
        self, status_code: int, detail: Any = None, headers: dict = None
    ) -> None:
        super().__init__(status_code=status_code, detail=detail)
        self.headers = headers

Таким образом, мы обычно можем напрямую использовать повышение, чтобы выбрасывать исключения для исключения.

5.2 HTTPException и возврат нового пользовательского заголовка запроса

from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}


@app.get("/items-header/{item_id}")
async def read_item_header(item_id: str):
    if item_id not in items:
        raise HTTPException(
            status_code=404,
            detail="Item not found",
            headers={"X-Error": "There goes my error"},
        )
    return {"item": items[item_id]}

5.3 Пользовательский возврат HTTPException

Как и в предыдущем случае с Bottle, мы единообразно обрабатываем возврат, добавляя пользовательскую глобальную ошибку. FastAPI также предоставляет механизм настройки ошибок:

Официальный пример выглядит следующим образом:

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse


class UnicornException(Exception):
    def __init__(self, name: str):
        self.name = name


app = FastAPI()


@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
    return JSONResponse(
        status_code=418,
        content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
    )


@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
    if name == "yolo":
        raise UnicornException(name=name)
    return {"unicorn_name": name}
    

Наблюдайте за результатом запроса:

Когда имя запроса == yolo, мы берем на себя инициативу генерировать UnicornException, и мы, @ app.exception_handler (UnicornException), также захватываем исключение с соответствующей информацией и возвращаем соответствующую информацию.

5.4 Переопределение обработки исключений FastAPI по умолчанию

Согласно официальной документации, RequestValidationError будет выброшен, если запрос содержит неверные данные или когда параметры отправлены ненормально.

На самом деле, я также могу переопределить и переписать информацию, возвращаемую нашим RequestValidationError, через приведенное выше пользовательское исключение:

как: Если код по умолчанию не добавляет обработку покрытия: Когда возникает исключение, подсказка:

from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException

app = FastAPI()


@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
    return PlainTextResponse(str(exc.detail), status_code=exc.status_code)


# @app.exception_handler(RequestValidationError)
# async def validation_exception_handler(request, exc):
#     return JSONResponse({'mes':'触发了RequestValidationError错误,,错误信息:%s 你妹的错了!'%(str(exc))})



@app.get("/items/{item_id}")
async def read_item(item_id: int):
    if item_id == 3:
        raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
    return {"item_id": item_id}



if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app='main4:app', host="127.0.0.1", port=8000, reload=True, debug=True)

Возвращено по ненормальному запросу:

При возобновлении покрытия:

from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException

app = FastAPI()


@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
    return PlainTextResponse(str(exc.detail), status_code=exc.status_code)


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
    return JSONResponse({'mes':'触发了RequestValidationError错误,,错误信息:%s 你妹的错了!'%(str(exc))})


@app.get("/items/{item_id}")
async def read_item(item_id: int):
    if item_id == 3:
        raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
    return {"item_id": item_id}



if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app='main4:app', host="127.0.0.1", port=8000, reload=True, debug=True)

Запрос:

На самом деле мы можем изменить возврат следующим образом и указать код ответа:

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
    )

инструкция:


# 注意fastapi包中的HTTPException才可以定义请求头
from fastapi import Depends, status, HTTPException
# from starlette.exceptions import HTTPException

6: промежуточное ПО FastAPI

Так называемое промежуточное ПО на самом деле такое же, как промежуточное ПО в нашем флаконе. Некоторые методы или операции необходимо выполнять перед всеми маршрутами, например, необходимо добавить перехватчик для http-доступа, а некоторые интерфейсы, требующие авторизации для доступа к API, можно проверить.

FastAPI предоставляет @app.middleware("http"), который может выполнять функцию перехвата, аналогичную вышеописанной. На самом деле, это очень похоже на функцию крючка для бутылки или фляги.

Пример выглядит следующим образом:

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

import time
from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException

app = FastAPI()


@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
    return PlainTextResponse(str(exc.detail), status_code=exc.status_code)


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
    return JSONResponse({'mes':'触发了RequestValidationError错误,,错误信息:%s 你妹的错了!'%(str(exc))})


@app.get("/items/{item_id}")
async def read_item(item_id: int):

    return {"item_id": item_id}


@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    return response


if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app='main4:app', host="127.0.0.1", port=8000, reload=True, debug=True)

Затем, после завершения запроса, мы обнаруживаем, что в заголовке нашего ответа есть новый заголовок запроса:

7: междоменная обработка FastAPI

Зачем нужна междоменная обработка Обычно наш API вызывается на фронтенд, но доменное имя, используемое фронтендом, может отличаться от имени домена API, которое не предоставляется, что приводит к тому, что браузер проблема политики одного и того же происхождения, поэтому нам нужно выполнить междоменную обработку.Поддержка доменных запросов.

Если FastAPI поддерживает кроссдоменность, можно добавить промежуточную форму, похожую на бутылку. Мало того, он также поддерживает междоменные запросы, ограниченные тем, какие домены поддерживаются:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    "http://localhost.tiangolo.com",
    "https://localhost.tiangolo.com",
    "http://localhost",
    "http://localhost:8080",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/")
async def main():
    return {"message": "Hello World"}

Мне лень иметь доступ к js, поэтому я пока не буду тестировать это, у меня будет возможность протестировать и проверить это позже.

8: Зависит от внедрения зависимостей FastAPI (дополнение)

Глядя на использование официального сайта, кажется, что это, кажется, больше понимает, поэтому мне все равно нужно снова учиться, чтобы узнать о зависимости.

Во-первых, внедрение зависимостей может быть функцией или классом, например внедрение зависимостей в виде следующей функции:

8.1 Простое описание зависимости

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    commons.update({'小钟':'同学'})
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons

if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app='main:app', host="127.0.0.1", port=8100, reload=True, debug=True)

Разберем процесс запроса интерфейса:

  • 1: Вышеупомянутое общее: dict = Depends(common_parameters) объявляет зависимость: Depends(common_parameters): объявляет зависимость интерфейса, указывая, что запрос параметра интерфейса зависит от функции common_parameters.

    При вызове интерфейса функция common_parameters вызывается для обработки запроса.

  • 2: функция Common_parameters в основном отвечает за получение функций, возврат словаря после обработки,

  • 3: Затем передайте результаты, возвращенные Depends (Common_Parameters), в Commons: Dict, это процесс, зависящий от инъекции.

Итак, в приведенном выше примере common_parameters — это наш зависимый объект.

Для этого зависимого объекта требования к запросу интерфейса следующие:

  • Необязательный параметр запроса q, который является строкой.
  • Необязательный параметр запроса skip, который имеет значение int, по умолчанию 0.
  • Необязательный предел параметра запроса, который является целым, по умолчанию 100.
  • вернуть словарь

Пример запроса:

Этот метод внедрения зависимостей на самом деле довольно удобен, похож на метод декораторов интерфейсов, например, в common_parameters мы можем сначала проверить и перехватить связанные параметры, а затем передать их. Сцена может быть похожа на наш предыдущий декоратор бутылок:

  • Та же логическая обработка суждений
  • аутентификация пользователя

8.2 Обработка классов как зависимых

Наши зависимые объекты выше представлены в виде функций, и FastAPI также поддерживает их выражение в виде классов. Согласно официальному сайту, зависимый объект должен быть вызываемым объектом, таким как: класс, функция и т.д...

Давайте посмотрим на форму класса:

from fastapi import Depends, FastAPI

app = FastAPI()


fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


class CommonQueryParams:
    def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
    response = {}
    # 如果q存在
    if commons.q:
        # 我们就把q加到一个新字典
        response.update({"q": commons.q})
        response.update({"小钟": '同学'})
    #然后在我们的fake_items_db进行截取
    items = fake_items_db[commons.skip : commons.skip + commons.limit]
    response.update({"items": items})
    return response

Над нашим CommonQueryParams находится класс, похожий на мою функцию, при вызове нашего интерфейса инициализируется объект класса. Согласно официальному сайту: Commons: CommonQueryParams = Depends (CommonQueryParams) и Commons = Depends (CommonQueryParams) эквивалентны. Другой: Commons: CommonQueryParams = Зависит ()

Пример запуска демо

Есть Q-параметры:

Без параметра Q:

8.3 Несколько уровней вложенных зависимостей

Многоуровневая вложенность означает, что классы могут быть классами. Функции могут зависеть от функций. По сути, это то же самое, что и наша предыдущая проверка параметров.

Например следующий код:

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str = None):
   return q


def query_or_cookie_extractor(
   q: str = Depends(query_extractor), last_query: str = Cookie(None)
):
   if not q:
       return last_query
   return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
   return {"q_or_cookie": query_or_default}

На самом деле это означает, что query_or_cookie_extractor зависит от query_extractor, а затем в интерфейс вводятся зависимые объекты query_or_cookie_extractor.

Скриншот официального сайта показывает вышеописанный процесс выполнения интерфейса следующим образом:

Для одной и той же зависимости, если результат обработки одинаковый, то есть возвращаемое значение одинаковое, мы можем вызвать зависимость несколько раз, при этом мы можем установить, использовать ли механизм кэширования для зависимого объекта:

async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
  return {"fresh_value": fresh_value}

8.4 список зависимостей списка

Давайте сначала посмотрим на официальный пример кода:


from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()


async def verify_token(x_token: str = Header(...)):
 if x_token != "fake-super-secret-token":
     raise HTTPException(status_code=400, detail="X-Token header invalid")


async def verify_key(x_key: str = Header(...)):
 if x_key != "fake-super-secret-key":
     raise HTTPException(status_code=400, detail="X-Key header invalid")
 return x_key


@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
 return [{"item": "Foo"}, {"item": "Bar"}]

Приведенный выше код означает проверку информации заголовка нашего запроса Заголовок, потому что в примере есть... три точки, указывающие на то, что это обязательное поле:

Проанализировав приведенный выше код, запустите его, чтобы увидеть результаты:

1: Если параметр заголовка не передается, это указывает на то, что наш параметр заголовка является ненормальным.

2: параметры головы заполнены:

Примечание: формат представления параметра, поскольку это параметр заголовка, поэтому x_token в нашем коде следует писать: x-token.

Плохой пример:

Следовательно, зависимости в приведенном выше списке означают, что условия должны быть выполнены за два дня до прохождения. Это чувство используется еще позже!

8.5 Внедрение многозависимых объектов и списки на самом деле одно и то же:

from fastapi import Depends, FastAPI


from fastapi import Depends, FastAPI, Header, HTTPException
from fastapi import Depends, FastAPI

app = FastAPI()




async def verify_token(x_token: str = Header(...)):
  if x_token != "fake-super-secret-token":
      raise HTTPException(status_code=400, detail="X-Token header invalid")
  return x_token

async def verify_key(x_key: str = Header(...)):
  if x_key != "fake-super-secret-key":
      raise HTTPException(status_code=400, detail="X-Key header invalid")
  return x_key


@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
  return [{"item": "Foo"}, {"item": "Bar"}]


@app.get("/items2/")
async def items2(xt: str = Depends(verify_token),xk: str = Depends(verify_key)):
 return {"xt": xt,'xk':xk}



if __name__ == '__main__':
  import uvicorn
  uvicorn.run(app='main:app', host="127.0.0.1", port=8100, reload=True, debug=True)

Например, вышеупомянутый XT: STR = зависит (Verify_Token), Xk: STR = зависит (Verify_key), также требует установления двух условий.

нормальная ситуация:

Нештатная ситуация:

Суммировать

Статья в целом следует идее официального документа и кратко разбирает его, позже наш API будет использовать некоторые точки знаний. Следующим шагом будет попытка сделать простые леса на основе вышеизложенных знаний.

END

Одноклассник Сяо Чжун | Текст [Оригинал] [Пожалуйста, свяжитесь со мной для перепечатки] | QQ: 308711822