FastAPI (python) - инструмент для быстрого создания Веб сервиса (WSGI) с REST api

19.04.21

Интеграция - WEB-интеграция

Ознакомительная статья по FastAPI (python) - инструменту быстрого создания Веб сервиса (WSGI) с REST api.

В своей практике, неоднократно сталкивался с необходимостью развертывания внешнего по отношению к 1С Веб-сервиса (почти всегда это был именно REST). Городить много таких сервисов на 1С - просто не удобно, плюс ограничения 1С и т.д. и т.п. Нужно сказать, что для реализации Веб сервисов придумано множество инструментов на всевозможных языках и платформах. Многие чуть лучше в одном, другие - в другом, однако, по итогу, самым простым и удобным мне показался именно FastAPI.  

FastAPI - очень простой и довольно быстрый фреймворк, предназначенный именно для организации REST API сервисов. Фреймворк написан на python, может работать на различных операционках, снабжен хорошей документацией. Главное удобство его использования заключается в простоте кодинга, а кроме того - в автоматическом формировании документации по вашему API в формате Open API. Кроме этого, в FastAPI имеется встроенная асинхронность, валидация параметров запросов. Официальный репозитарий проекта - тут, документация - тут.

В статье я не буду разбирать подробно FastAPI, цель статьи - ознакомительная (просто надеюсь, что кому-то из 1С-ов это будет полезно). Вопросы архитектуры приложения, использования ORM, асинхронности и многопоточности, производительности, особенностей использования валидаторов и т.п. - здесь не рассматриваются.

 

Итак, начнём. Первое что нужно для работы - python. Пакет можно скачать отсюда - https://www.python.org. Далее, нужна среда разработки. Рекомендую использовать pycharm - тут (у них есть бесплатная версия Community), но вполне подойдет и Visual Studio Code (тоже легко найдёте).
Надеюсь у вас получилось всё скачать и установить, а если нет, то смотрите информацию в Интернете - её море.

Далее, создаём новый проект и устанавливаем FastAPI:

pip install fastapi

Теперь установим wsgi сервер, с которым работает FastAPI:

pip install uvicorn

Вот в принципе и вся установка. 

Окей, далее, в папке проекта создадим файл main.py (если конечно среда разработки его не создала). Напишем в нем такой код: 

import uvicorn

if __name__ == '__main__':
    uvicorn.run(
        "app:app",
        host='localhost',
        port=8080,
        reload=True
    )

здесь мы описываем запуск wsgi сервера и приложения под именем app.py на нём.

Теперь создадим в папке проекта сам файл app.py:

from fastapi import FastAPI

app = FastAPI()

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

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

Не в даваясь в детали, мы здесь описали наш api, правда пока только два метода(функции). Видно, что это обработчики GET запросов, а также адреса методов. Сохраняем файлы. Проверим работоспособность.

В терминале пишем:

main.py

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

Ошибок нет, сервер стартанул. Наше API работает? Проверим. Переходим в браузер:

 

Перейдём по адресу http://localhost:8080/docs:

Это и есть интерактивная документая (документация доступна также и по адресу - http://localhost:8080/redoc, но немного в другом формате). Обратите внимание на то, что кнопки активны, можно вручную передавать туда параметры и смотреть результаты. Это своего рода консоль (swagger).

В нашем примере реализовано лишь две очень простые функции, вызываемые GET запросами. Однако, вы можете создавать и PUT, и DELETE, и POST запросы. Для этого, в декораторе необходимо лишь указать необходимый метод. Подробности ищите в документации и примерах к ней. Мы же в завершении статьи, прикрутим к нашему API базу данных.

Самым простым вариантом при выборе базы данных в python является SQLite. Это файловая база данных, идущая "в коробке" с python. Разумеется, вы можете установить и настроить использование других СУБД, но сейчас это не требуется. Для примера, пусть в базе будет только одна таблица items из которой и будут извлекаться данные для функций API.

Для удобства, создадим в проекте два новых файла: controller.py - для описания финкций, database_scripts - для описания взаимодействия с базой. Изменим также и app.py - здесь нужно подключить файл controller.py для вызова соответствующих функций. Для данной задачи мне удобно было организовать модули именно так.

app.py:

from fastapi import FastAPI
import controller as controller

tags_metadata = [
    {
        'name': 'items',
        'description': 'items',
    }
]

app = FastAPI(
    title='Mini api',
    description='This is my mini api (description)',
    version='1.0.0',
    openapi_tags=tags_metadata
)

@app.get("/")
async def root():
    return controller.get_items()

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

 

controller.py:

import database_scripts as db_scripts

def get_items():
    return db_scripts.select_items()

def get_item(item_id):
    results = db_scripts.select_items(item_id)
    return results

 

database_scripts.py:

import sqlite3 as sq

# Параметры базы данных
DATABASE = 'database.db'
SCHEME = 'schem.sql'


# Подключение базы данных
def connect_db():
    return sq.connect(DATABASE)


# Создание базы данных (по скрипту из SCHEME)
def create_db():
    connection = connect_db()
    cursor = connection.cursor()
    with open(SCHEME, mode='r') as file:
        scheme_script = file.read()
    cursor.executescript(scheme_script)
    connection.commit()
    cursor.close()
    connection.close()


# Вспомогательная функция для упрощения получения результатов по ключу
def dict_factory(cursor, row):
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d


def select_items(item_id=None):
    my_list = []
    connection = connect_db()

    if item_id == None:
        sql = "SELECT * FROM items"
    else:
        sql = f"SELECT * FROM items WHERE id = {item_id}"

    try:
        cursor = connection.execute(sql)
        cursor.row_factory = dict_factory
        for row in cursor:
            record = {
                'name': row.get('name'),
                'id': row.get('id'),
                'folder_id': row.get('folder_id'),
                'is_weighted': row.get('is_weighted'),
                'description': row.get('description'),
            }
            my_list.append(record)

    except connection.Error as error:
        print("Error connection to database", error)
    finally:
        if connection: connection.close()

    return my_list

 

Кроме этого, создадим в каталоге проекта также и файл schem.sql, где напишем скрипт создания нашей базы (всего одна таблица, но если потребуется больше, то как и в любой СУБД - таблицы описываются через точку с запятой):

CREATE TABLE IF NOT EXISTS items (
    id integer PRIMARY KEY,
    name char(150) NOT NULL,
    folder_id integer,
    is_weighted boolean,
    description text
);

 

Думаю что то, что описано в файлах ясно всем - из модуля app.py вызываются соответствующие функции из controllers.py, где при необходимости из database_scripts.py вызываются функции, связанные с взаимодействием с базой данных.

Ок. Теперь осталось создать саму базу и заполнить её чем-то. Создавать и заполнять базу можно вручную (н-р с помощью DB Browser for SQLite), либо выполнив такой незамысловатый код в консоли: 

>>> from database_scripts import create_db
>>> create_db()

 

Для заполнения можно добавить в database_scripts.py такой код:

items = [
    (1, 'Вентилятор BINATONE ALPINE 160вт, напольный ,', 1, 0, 'Вентилятор BINATONE ALPINE 160вт, напольный , оконный'),
    (2, 'Вентилятор JIPONIC (Тайв.),', 1, 0, 'Вентилятор JIPONIC (Тайв.), напольный'),
    (3, 'Вентилятор настольный', 1, 0, 'Вентилятор настольный'),
    (4, 'Вентилятор ОРБИТА,STERLING,ЯП.', 1, 0, 'Вентилятор ОРБИТА,STERLING,ЯП.'),
    (5, 'Пылесос "Омега" 1250вт', 1, 0, 'Пылесос "Омега" 1250вт'),
    (6, 'Телевизор "SHARP"', 3, 0, 'Телевизор "SHARP"'),
    (7, 'Набор кухонной мебели (цвет белый)', 4, 0, 'Набор кухонной мебели (цвет белый)'),
]


def insert_testdata():
    connection = connect_db()
    cursor = connection.cursor()
    cursor.executemany("INSERT INTO items VALUES(?, ?, ?, ?, ?)", items)
    connection.commit()
    connection.close()

а затем выполним его в консоли:

>>> from database_scripts import insert_testdata
>>> insert_testdata()

 

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

 

Вот собственно и всё. Перезапустим сервер и проверим работу API.

ну и второй API метод:

 

На этом всё. Чтож, надеюсь мой поток мыслей был вам понятен, а информация окажется хоть немного полезной. Как видите, почти без программирования (и без ООП) вполне себе можно быстро и легко создавать REST API. Конечно, здесь нет ни валидации, ни ORM или моделей, нет даже авторизации, а многие вещи даже и вовсе не упоминались, но это и не в ходило в цели обзора.

Исходный код из статьи.

FastAPI REST

См. также

Сайты и интернет-магазины Интеграция WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 Управленческий учет Платные (руб)

Интеграция 1С и Битрикс 24. Разработка имеет двухстороннюю синхронизацию 1С и Bitrix24 задачами. Решение позволяет создавать пользователя в 1С из Битрикс24 и наоборот. Данная разработка технически подходит под все основные конфигурации линейки продуктов 1С:Предприятие 8.3 (платформа начиная с 8.3.23). При приобретении предоставляется 1 месяц бесплатных обновлений разработки. Доступна демо-версия продукта с подключением Вашего Битрикс24

5040 руб.

04.05.2021    19018    10    16    

16

Управление взаимоотношениями с клиентами (CRM) WEB-интеграция Анализ продаж Системный администратор Программист Пользователь Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Управленческий учет Платные (руб)

Подсистема интеграции Amo CRM с 1С: технические требования, порядок работы, возможности, доработки и обновления. Бесплатный период техподдержки - 1 месяц.

60000 руб.

07.05.2019    31883    62    40    

23

Сайты и интернет-магазины WEB-интеграция Системный администратор Программист Пользователь Платформа 1С v8.3 Конфигурации 1cv8 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM разных брендов в одной информационной базе в ручном и автоматическом режиме.

36000 руб.

03.08.2020    16773    15    19    

15

WEB-интеграция Программист Платформа 1С v8.3 Бухгалтерский учет 1С:Бухгалтерия 3.0 Бытовые услуги, сервис Платные (руб)

Внешняя обработка разрабатывалась для загрузки документов из Ветменеджер в 1С: Бухгалтерия 3.0

12000 руб.

02.02.2021    17007    45    49    

26

WEB-интеграция 8.3.8 Конфигурации 1cv8 Автомобили, автосервисы Беларусь Украина Россия Казахстан Управленческий учет Платные (руб)

Расширение предназначено для конфигурации "1С:Предприятие 8. Управление Автотранспортом. ПРОФ". Функционал модуля: 1. Заполнение регистров сведений по подсистеме "Мониторинг", а именно: события по мониторингу, координаты по мониторингу, пробег и расход по мониторингу, текущее местоположение ТС по мониторингу 2. Заполнение путевого листа: пробег по мониторингу, время выезда/заезда, табличная часть ГСМ, места стоянок по геозонам. 3. Отчеты по данным загруженным в регистры сведений. 4. Предусмотрена автоматическая загрузка данных в фоновом режиме (условия работы данной загрузке читайте в описании товара) Модуль работает без включенной константы по настройкам мониторинга. Модуль формы предоставляется с открытым кодом, общий модуль защищен. Любой заинтересованный пользователь, имеет возможность скачать демо-версию расширения.

22656 руб.

25.05.2021    13382    36    8    

15
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. informa1555 2695 19.04.21 09:51 Сейчас в теме
Отличная статья!
Идальго; +1 Ответить
2. Nubsdale 19.04.21 10:30 Сейчас в теме
Автор, объясните, почему вы прикручивали всё это к SQLite, а не к 1С ?
3. Идальго 229 19.04.21 10:34 Сейчас в теме
(2) Ну мне так нравится, это удобнее и быстрее, всё из коробки)) Ну и на сервере может и не быть 1С. Потом, я вот не знаю, с 1С (которая файловая база) вообще работают кроме как из самой 1С...
4. Nubsdale 19.04.21 10:37 Сейчас в теме
(3) Статья у вас интересная, вот только было здорово, если бы на 1Сном форуме, была 1С, а не SQLite
kar911; freelog; ellavs; +3 2 Ответить
5. Идальго 229 19.04.21 10:41 Сейчас в теме
(4) Аххаа))) В моей практике, когда я занимался 1С разработкой, довольно часто возникала задача из статьи (быстренько с минимальными условиями поднять REST API микросервис). Уверен, что многим 1С разработчикам материал будет полезен и позволит сэкономить свое время на решение подобной задачи. Именно поэтому я опубликовал статью на форуме про 1С )))
Alex_Iz; Deslime; mnemchinov; JohnyDeath; Upiterus; Quasar; nazirovramzil; v3n7; +8 Ответить
6. user925427 126 19.04.21 11:04 Сейчас в теме
Сама статья написана понятно, ссылки для желающих попробовать работу с внешним API даны.
На мой взгляд, не хватает описания класса задач, для которых применимо данное решение,
так как не для любой задачи оно оптимально.
Зачем нужна дополнительная SQL-база? Автор говорит, что "на 1С - просто не удобно, плюс
ограничения 1С и т.д. и т.п."? Полагаю, что из неё быстрее возвращать информацию по запросам.
Но в этом случае понадобится обмен между 1С и этой базой, а актуальность информации в SQL-базе
будет определяться частотой этого обмена. Если же "создавать и PUT, и DELETE, и POST запросы",
то обмен понадобится двусторонний для актуализации информации в 1С. О необходимости такого
обмена стоило бы упомянуть. А за доступность описания однозначный плюс.
7. Идальго 229 19.04.21 11:32 Сейчас в теме
(6) Ну не знаю, для ознакомительной статьи достаточно и материала, и описания, и функционала. Да вот даже в самой первой строке статьи написано зачем это и для чего. Если говорить в контексте 1С, то знаете, когда мне нужно на базе 1С-ки поднять REST API или там SOAP(WSDL), то я прям объектом метаданных соответствующим из самой 1С и воспользуюсь, м.б. OData прикручу. Но, что делать, если вам срочно понадобился вспомогательный REST API сервис (н-р большая справочная база, ФИАС, микросервис, да что угодно). Не городить же для этого ещё одну базу 1С, когда есть более удобные и быстрые механизмы.
19. NoRazum 29 26.04.21 10:00 Сейчас в теме
(6) Статья годная. Тоже столкнулся при чем тут 1С? и в каком кейсе это может понадобиться?
Кто может прояснить.
8. SaschaG 196 19.04.21 16:27 Сейчас в теме
Как-то выглядит излишне. В 1С есть http-сервисы, которые позволяют реализовать это. А то ведь шанс отказа системы - это шанс отказа любого из ее элементов, а тут мы накидываем сверху еще 2.
Kopitsa.k; +1 Ответить
9. pm74 201 19.04.21 20:20 Сейчас в теме
(0) еще раскройте тему как запустить сервис, желательно в винде , желательно venv
кстати unicorn под win работает ? gunicorn насколько помню только unix
точно работает Waitress
10. Идальго 229 19.04.21 20:43 Сейчас в теме
(9) оно на всём работает)) В статье написано как запустить и настроить. Ставите питон и пайчарм, делаете файлики как в первой части статьи. Создаете проект, внизу есть терминал - прямо в нём пишите имя файла.
11. pm74 201 19.04.21 21:01 Сейчас в теме
(10) видимо чего то я не уловил , ткните меня в то место в тексте, где написано как виндовый сервис на питоне поднять
12. Идальго 229 19.04.21 21:09 Сейчас в теме
(11) Кажется я понял.. видимо вы хотите запускать программу как службу виндоуз (чтобы в списке сервисов было). Кхм, сорри, но я не знаю. Я делал запускатель - батник с прописанной строкой запуска, который стартовал при старте системы. Вообще, много вариантов гуглится, того как скрипт питона использовать в качестве службы виндоуз.
16. 1cnik2 23.04.21 07:43 Сейчас в теме
(11) следует курить
choco - менеджер пакетов для windows(https://chocolatey.org)
nssm - сервис-менеджер, позволяет регистрировать и запускать консольные программы как сервисы(установка - choco install nssm)
virtualenv - поднятие виртуального окружения python в отдельно взятом каталоге(это важная история, поскольку без этого при наличии более, чем одного подобного сервиса, они начнут конфликтовать по библиотекам, а virtualenv позволит установить библиотеки нужных версий именно для одного сервиса в виртуальное окружение)
alex_bob; morin; Alex_Iz; +3 Ответить
13. botokash 391 20.04.21 10:02 Сейчас в теме
Не совсем понял преимуществ создания сервисов на FastAPI перед тем же Flask. Только вижу автодокументацию из коробки, что во фласке решается отдельными либами.
14. Идальго 229 20.04.21 13:55 Сейчас в теме
(13) Ну с Фласком я и не сравнивал. Безусловно, на Фласке всё это тоже можно сделать, но всё же Фласк это больше веб-фреймворк (там же вот и шаблонизатор в коробке вроде Джинджа2), с асинхронностью тоже были вопросы. Ну и FastAPI все же вцелом по-проще, на мой взгляд.
17. 1cnik2 23.04.21 07:46 Сейчас в теме
(13) основные преимущества fastapi перед flask - асинхронность, проверка входящих данных и автодокументация
15. malikov_pro 1312 20.04.21 17:54 Сейчас в теме
(0) Не описан вариант организации сервиса из app.py, руками запускать и держать открытой консоль неудобно.

Уточните пожалуйста контекст использования, потому что от него зависит выбор инструмента. Например мне для доступа к MSSQL по HTTP удобнее express.js использовать, т.к. файл один и запускается в службу через pm2.

Если нужна обертка для SQL, то посмотрите на PostgREST, в нем нужно только сформировать структуру БД, автодок присутствует.

Если в рамках python возможно будет удобно https://docs.python-eve.org/en/stable/.

По форматированию:
Код можно организовать под спойлеры, чтобы основное повествование было видно.

(4) В каком контексте хотите использовать rest proxy перед 1С? Если организовать аутентификацию, валидацию и подобное, то можно разобрать данный вопрос, в самой 1С на данный момент с этим туго.
Alex_Iz; Идальго; +2 Ответить
21. Aletar 09.06.21 09:11 Сейчас в теме
(15) А можете подсказать где посмотреть на варианты организации сервиса из app.py, сходу что-то не нашел.
18. JohnyDeath 301 23.04.21 12:23 Сейчас в теме
Еще есть вот такой проект, где пишем на привычном 1С-языке и имеем почти все плюсы GO
https://github.com/covrom/gonec
Скрипт может быть скомпилирован в один исполняемый файл. Работает под любой ОС
Alex_Iz; Идальго; +2 Ответить
20. Aletar 09.06.21 09:08 Сейчас в теме
(18) Проект, судя по всему, уже не развивается, последний коммит 3 года назад. Можно, конечно, ещё OneScript.Web вспомнить, но там, вроде как, тоже сильно развития нет.
23. Aletar 10.06.21 04:50 Сейчас в теме
24. user1637485 29.07.21 08:42 Сейчас в теме
Касаемо БД 1С
Нет ничего сложного прикрутить из FastAPI запросы к БД 1С
Можно часть информации хранить в mySQL/постгре, другую в 1С
API в этом тем и хорош, что под капотом у вас может быть несколько БД... редис и т.п. Даже несколько серверов с FastAPI, каждый из которых будет отвечать за свои эндпоинты. Т.е. разделить нагрузку на разные сервера.
Такое на вордпресе не реализовать )

П.С. У меня FastAPI работает с постгрес БД + часть информации берется из MSSQL БД

Автор статьи привёл самые основы, без углубления в сериализацию/десериализацию запросов в Pydantic, без тестов.
25. user1649582 19.08.21 15:00 Сейчас в теме
изучаю материал, спасибо за статью.
вот только не могу понять ) почему у меня при обращении на http://localhost:8080/3 выдает {"item_id":3} вместо [{ "name:Вентилятор настольный" .....
26. pashamix 14.04.22 05:51 Сейчас в теме
Очень круто! Интересно!
Сам начал недавно изучать Python, в планах построить мини erp систему.
На 1С писал простые конфигурации с нуля, для учета деталей ПК и их сборок/разборок.
27. Amadeus8 14.04.22 11:32 Сейчас в теме
Спасибо за статью! А как передать несколько параметров в REST что-то типа http://localhost:2000/custom?brand=dell&limit=20&price=20000&sort=asc
и распарсить их в питоне?
30. sttt 116 20.04.22 01:05 Сейчас в теме
(27)
Как-то так:
import uvicorn

fr om typing import List
from typing import Optional

from fastapi import FastAPI

from pydantic import BaseModel


tags_metadata = [
    {
        'name': 'items',
        'description': 'items',
    }
]


app = FastAPI(
    title='Бренд api',
    description='Запросы по брендам',
    version='1.0.0',
    openapi_tags=tags_metadata
)


@app.get("/custom/")
async def get_custom(brand: str, price: int, lim it: int = 10, sort: str = "desc", skip: Optional[int] = 0 ): 
	return {"brand": brand, "price": price}


if __name__ == '__main__':
    uvicorn.run(
        "main:app",
        host='localhost',
        port=8888,
        reload=True
    )
Показать
28. Amadeus8 14.04.22 11:55 Сейчас в теме
Вопрос даже не в том как передать несколько параметров, а как передать list в качестве параметра?
29. Идальго 229 14.04.22 19:06 Сейчас в теме
(28) Чтобы передавать в ГЕТ запросе несколько параметров, вам не плохо бы на стороне сервиса обеспечить их поддержку. Выдергивать параметры и их значения можно несколькими способами - либо самому парсить строку(читаете, через разделитель "&" - пары ключ и значение), либо средствами фреймворка(это предпочтительный вариант). Учтите только, что для ГЕТ запросов, обычно, существует ограничение на длину строки(строка м.б. длинная и в 99% случаев вам ее хватит, но иногда может не хватить).
32. sttt 116 20.04.22 01:17 Сейчас в теме
(28) Чтобы массив значений нужно установить "pip install typing"
Потом:

from typing import List
from fastapi import FastAPI
from fastapi import Query

app = FastAPI()

@app.get("/custom-list/")
async def read_custom(q: List[str] = Query(["foo", "bar"])):
    query_items = {"q": q}
    return query_items
Показать



В браузере:
http://localhost:8888/custom-list/?q=test1&q=test2
31. sttt 116 20.04.22 01:06 Сейчас в теме
А в адресной строке браузера нужно будет так запрашивать:
http://localhost:8888/custom/?brand=dell&limit=20&price=20000&sort=asc
33. MarryJane 31 13.06.22 13:08 Сейчас в теме
Ошибаюсь (как сказал один киногерой .. я только учусь), но fastApi реализует спецификацию ASGI. И для того что бы его запустить на WSGI нужно запускать через обертку. Например что бы сервис запустить на IIS нужно вместо:
import uvicorn

if __name__ == '__main__':
uvicorn.run(
"app:app",
host='localhost',
port=8080,
reload=True
)

вот так
from a2wsgi import ASGIMiddleware
...определение fastapi

wsgi_app = ASGIMiddleware(app)
Оставьте свое сообщение