python

Пишем API на Python (с Flask и RapidAPI)

  • пятница, 23 августа 2019 г. в 00:23:07
https://habr.com/ru/company/skillbox/blog/464705/
  • Блог компании Skillbox
  • Python
  • Программирование
  • API
  • Учебный процесс в IT




Если вы читаете эту статью, вероятно, вы уже знакомы с возможностями, которые открываются при использовании API (Application Programming Interface).

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

Ответ прост: нужно создать собственный API.

Несмотря на то, что это поначалу кажется сложной задачей, на самом деле всё просто. Мы расскажем, как это сделать с помощью Python.

Что нужно для начала работы


Для разработки API необходимы:
  • Python 3;
  • Flask — простой и легкий в использовании фреймворк для создания веб-приложений;
  • Flask-RESTful — расширение для Flask, которое позволяет разработать REST API быстро и с минимальной настройкой.

Установка выполняется командой:

pip install flask-restful


Рекомендуем бесплатный интенсив по программированию для начинающих:
Разработка telegram-бота на C# — 26–28 августа. Бесплатный интенсив, который позволяет разобраться в том, как работают боты-помощники, в особенностях работы с API Telegram и прочих нюансах. Трое лучших участников получат от Skillbox 30 000 рублей.

Перед тем как начать

Мы собираемся разработать RESTful API с базовой CRUID-функциональностью.

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

Что такое REST?

REST API (Representational State Transfer) — это API, которое использует HTTP-запросы для обмена данными.

REST API должны соответствовать определенным критериям:
  • Архитектура клиент-сервер: клиент взаимодействует с пользовательским интерфейсом, а сервер — с бэкендом и хранилищем данных. Клиент и сервер независимы, любой из них может быть заменен отдельно от другого.
  • Stateless — никакие клиентские данные не сохраняются на сервере. Состояние сеанса хранится на стороне клиента.
  • Кэшируемость — клиенты могут кэшировать ответы сервера для улучшения общей производительности.

Что такое CRUD?

CRUD — концепция программирования, которая описывает четыре базовых действия (create, read, update и delete).

В REST API типы запросов и методы запроса отвечают за такие действия, как post, get, put, delete.

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

Разработка


Давайте создадим репозиторий цитат об искусственном интеллекте. ИИ — одна из наиболее активно развивающихся технологий сегодня, а Python — популярный инструмент для работы с ИИ.

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

Начнем с импорта необходимых модулей и настройки Flask:
from flask import Flask
from flask_restful import Api, Resource, reqparse
import random
app = Flask(__name__)
api = Api(app)

В этом сниппете Flask, Api и Resource — классы, которые нам нужны.

Reqparse — это интерфейс парсинга запросов Flask-RESTful… Также понадобится модуль random для отображения случайной цитаты.

Теперь мы создадим репозиторий цитат об ИИ.

Каждая запись репо будет содержать:
  • цифровой ID;
  • имя автора цитаты;
  • цитату.

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

ai_quotes = [
    {
        "id": 0,
        "author": "Kevin Kelly",
        "quote": "The business plans of the next 10,000 startups are easy to forecast: " +
                 "Take X and add AI."
    },
    {
        "id": 1,
        "author": "Stephen Hawking",
        "quote": "The development of full artificial intelligence could " +
                 "spell the end of the human race… " +
                 "It would take off on its own, and re-design " +
                 "itself at an ever increasing rate. " +
                 "Humans, who are limited by slow biological evolution, " +
                 "couldn't compete, and would be superseded."
    },
    {
        "id": 2,
        "author": "Claude Shannon",
        "quote": "I visualize a time when we will be to robots what " +
                 "dogs are to humans, " +
                 "and I’m rooting for the machines."
    },
    {
        "id": 3,
        "author": "Elon Musk",
        "quote": "The pace of progress in artificial intelligence " +
                 "(I’m not referring to narrow AI) " +
                 "is incredibly fast. Unless you have direct " +
                 "exposure to groups like Deepmind, " +
                 "you have no idea how fast — it is growing " +
                 "at a pace close to exponential. " +
                 "The risk of something seriously dangerous " +
                 "happening is in the five-year timeframe." +
                 "10 years at most."
    },
    {
        "id": 4,
        "author": "Geoffrey Hinton",
        "quote": "I have always been convinced that the only way " +
                 "to get artificial intelligence to work " +
                 "is to do the computation in a way similar to the human brain. " +
                 "That is the goal I have been pursuing. We are making progress, " +
                 "though we still have lots to learn about " +
                 "how the brain actually works."
    },
    {
        "id": 5,
        "author": "Pedro Domingos",
        "quote": "People worry that computers will " +
                 "get too smart and take over the world, " +
                 "but the real problem is that they're too stupid " +
                 "and they've already taken over the world."
    },
    {
        "id": 6,
        "author": "Alan Turing",
        "quote": "It seems probable that once the machine thinking " +
                 "method had started, it would not take long " +
                 "to outstrip our feeble powers… " +
                 "They would be able to converse " +
                 "with each other to sharpen their wits. " +
                 "At some stage therefore, we should " +
                 "have to expect the machines to take control."
    },
    {
        "id": 7,
        "author": "Ray Kurzweil",
        "quote": "Artificial intelligence will reach " +
                 "human levels by around 2029. " +
                 "Follow that out further to, say, 2045, " +
                 "we will have multiplied the intelligence, " +
                 "the human biological machine intelligence " +
                 "of our civilization a billion-fold."
    },
    {
        "id": 8,
        "author": "Sebastian Thrun",
        "quote": "Nobody phrases it this way, but I think " +
                 "that artificial intelligence " +
                 "is almost a humanities discipline. It's really an attempt " +
                 "to understand human intelligence and human cognition."
    },
    {
        "id": 9,
        "author": "Andrew Ng",
        "quote": "We're making this analogy that AI is the new electricity." +
                 "Electricity transformed industries: agriculture, " +
                 "transportation, communication, manufacturing."
    }
]

Теперь нужно создать ресурсный класс Quote, который будет определять операции эндпоинтов нашего API. Внутри класса нужно заявить четыре метода: get, post, put, delete.

Начнем с метода GET

Он дает возможность получить определенную цитату путем указания ее ID или же случайную цитату, если ID не указан.

class Quote(Resource):
    def get(self, id=0):
        if id == 0:
            return random.choice(ai_quotes), 200
        for quote in ai_quotes:
            if(quote["id"] == id):
                return quote, 200
        return "Quote not found", 404

Метод GET возвращает случайную цитату, если ID содержит дефолтное значение, т.е. при вызове метода ID не был задан.

Если он задан, то метод ищет среди цитат и находит ту, которая содержит заданный ID. Если же ничего не найдено, выводится сообщение “Quote not found, 404”.

Помните: метод возвращает HTTP-статус 200 в случае успешного запроса и 404, если запись не найдена.

Теперь давайте создадим POST-метод для добавления новой цитаты в репозиторий

Он будет получать идентификатор каждой новой цитаты при вводе. Кроме того, POST будет использовать reqparse для парсинга параметров, которые будут идти в теле запроса (автор и текст цитаты).

def post(self, id):
      parser = reqparse.RequestParser()
      parser.add_argument("author")
      parser.add_argument("quote")
      params = parser.parse_args()
      for quote in ai_quotes:
          if(id == quote["id"]):
              return f"Quote with id {id} already exists", 400
      quote = {
          "id": int(id),
          "author": params["author"],
          "quote": params["quote"]
      }
      ai_quotes.append(quote)
      return quote, 201

В коде выше POST-метод принял ID цитаты. Затем, используя reqparse, он получил автора и цитату из запроса, сохранив их в словаре params.

Если цитата с указанным ID уже существует, то метод выводит соответствующее сообщение и код 400.

Если цитата с указанным ID еще не была создана, метод создает новую запись с указанным ID и автором, а также другими параметрами. Затем он добавляет запись в список ai_quotes и возвращает запись с новой цитатой вместе с кодом 201.

Теперь создаем PUT-метод для изменения существующей цитаты в репозитории

def put(self, id):
      parser = reqparse.RequestParser()
      parser.add_argument("author")
      parser.add_argument("quote")
      params = parser.parse_args()
      for quote in ai_quotes:
          if(id == quote["id"]):
              quote["author"] = params["author"]
              quote["quote"] = params["quote"]
              return quote, 200
      
      quote = {
          "id": id,
          "author": params["author"],
          "quote": params["quote"]
      }
      
      ai_quotes.append(quote)
      return quote, 201

PUT-метод, аналогично предыдущему примеру, берет ID и input и парсит параметры цитаты, используя reqparse.

Если цитата с указанным ID существует, метод обновит ее с новыми параметрами, а затем выведет обновленную цитату с кодом 200. Если цитаты с указанным ID еще нет, будет создана новая запись с кодом 201.

Наконец, давайте создадим DELETE-метод для удаления цитаты, которая уже не вдохновляет

def delete(self, id):
      global ai_quotes
      ai_quotes = [qoute for qoute in ai_quotes if qoute["id"] != id]
      return f"Quote with id {id} is deleted.", 200

Этот метод получает ID цитаты при вводе и обновляет список ai_quotes, используя общий список.

Теперь, когда мы создали все методы, всё, что нам нужно, — просто добавить resource к API, задать путь и запустить Flask.

api.add_resource(Quote, "/ai-quotes", "/ai-quotes/", "/ai-quotes/<int:id>")
if __name__ == '__main__':
    app.run(debug=True)

Наш REST API Service готов!

Далее мы можем сохранить код в файл app.py, запустив его в консоли при помощи команды:

python3 app.py

Если все хорошо, то мы получим нечто вроде этого:

* Debug mode: on
* Running on 127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: XXXXXXX


Тестируем API

После того как API создан, его нужно протестировать.

Сделать это можно при помощи консольной утилиты curl или клиента Insomnia REST либо же опубликовав API на Rapid API.



Публикуем наш API

RapidAPI — самый большой в мире маркетплейс с более чем 10 000 API (и около 1 млн разработчиков).

RapidAPI не только предоставляет единый интерфейс для работы со сторонними API, но и даtт возможность быстро и без проблем опубликовать ваш собственный API.

Для того чтобы сделать это, сначала нужно опубликовать его на каком-нибудь сервере в сети. В нашем случае воспользуемся Heroku. Работа с ним не должна вызвать никаких сложностей, (узнать о нём больше можно здесь).

Как опубликовать ваш API на Heroku

1. Устанавливаем Heroku.

Первым делом нужно зарегистрироваться и установить Heroku Command Line Interface (CLI). Это работает на Ubuntu 16+.

sudo snap install heroku --classic

Затем логинимся:

heroku login

2. Добавляем необходимые файлы.

Теперь нужно добавить файлы для публикации в папку в нашем приложении:
  • requirements.txt со списком необходимых Python модулей;
  • Procfile, который указывает, какие команды должны быть выполнены для запуска приложения;
  • .gitignore — для исключения файлов, которые не нужны на сервере
.
Файл requirements.txt будет содержать следующие строки:
  • flask
  • flask-restful
  • gunicorn

Пожалуйста, обратите внимание: мы добавили gunicorn (Python WSGI HTTP Server) в список, поскольку нужно запустить наше приложение на сервере.

Procfile будет содержать:

web: gunicorn app:app

Содержимое .gitignore:

*.pyc
__pycache__/

Теперь, когда созданы файлы, давайте инициализируем git-репо и закоммитим:

git init
git add
git commit -m «First API commit»


3. Создаем новое Heroku-приложение.

heroku create

Отправляем master branch в удаленный репо Heroku:

git push heroku master

Теперь можно начать, открыв API Service при помощи команд:

heroku ps:scale web=1
heroku open

API будет доступно по адресу your-random-heroku-name.herokuapp.com/ai-quotes.

Как добавить ваш Python API в маркетплейс RapidAPI

После того как API-сервис опубликован на Heroku, можно добавить его к Rapid API. Здесь подробная документация по этой теме.

1. Создаем аккаунт RapidAPI.



Регистрируем бесплатную учетную запись — это можно сделать при помощи Facebook, Google, GitHub.



2. Добавляем API в панель управления.



3. Далее вводим общую информацию о своем API.



4. После нажатия “Add API” появляется новая страничка, где можно ввести информацию о нашем API.



5. Теперь можно либо вручную ввести эндпоинты API, либо загрузить swagger-file при помощи OpenAPI.



Ну а теперь нужно задать эндпоинты нашего API на странице Endpoints. В нашем случае эндпоинты соответствуют концепции CRUD (get, post, put, delete).



Далее нужно создать эндпоинт GET AI Quote, который выводит случайную цитату (в том случае, если ID дефолтный) или цитату для указанного ID.

Для создания эндпоинта нужно нажать кнопку “Create Endpoint”.



Повторяем этот процесс для всех других эндпоинтов API. На этом всё! Поздравляю, вы опубликовали ваш API!

Если все хорошо, страничка API будет выглядеть как-то так:



Заключение


В этой статье мы изучили процесс создания собственного RESTful API Service на Python, вместе с процессом публикации API в облаке Heroku и добавлением его в каталог RapidAPI.

Но в тестовом варианте были показаны только базовые принципы разработки API — такие нюансы, как безопасность, отказоустойчивость и масштабируемость, не рассматривались.

При разработке реального API все это нужно учитывать.