Привет, Хабр! На связи хостинг-провайдер SpaceWeb. В этой статье расскажем, как работает клиентский API у нас, почему для него мы выбрали технологию JSON-RPC и чем она нам так нравится. Историей делятся Виталий Киреев, руководитель R&D, и Алексей Шашкин, продакт-менеджер.

Бэкграунд проекта: API как панель управления 

Мы начали развивать API для наших продуктов еще 6 лет назад. Тогда у нас была задача сделать панель управления, которая отвечала бы современным стандартам: мобильное приложение, SPA. Но реализовать это «в лоб» было сложно, потому что у нас был большой legacy-проект — монолит с парадигмой MVC. Все инструменты внутри были очень связаны, правки было вносить практически невозможно. 

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

Архитектура проекта была изначально заложена правильно: бэкенд-слой был выделен отдельно и не связан с фронтенд-частью, а значит и клиент мог быть абсолютно любым. Для того, чтобы вызвать API, можно использовать разные транспорты: и HTTP, и брокер сообщений на RabbitMQ. И каждый слой сервисов имеет эффективное покрытие юнит-тестами. 

API для клиентов

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

За основу взяли API для панели управления. При этом мы понимали, что какие-то методы нужно зарефакторить и изменить по количеству параметров, потому что они могут быть не очевидны для клиента. Например, в API панели управления есть возвращаемые значения, которые нужны для отображения значений. А какому-то разработчику они будут не нужны. 

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

Какую технологию выбрали для API SpaceWeb

Для доступа к API используется протокол HTTP/HTTPS, а данные передаются в виде JSON-RPC по методу POST. Вызываемый метод API содержится в запросе вместе с параметрами вызова. 

Мы выбрали именно JSON-RPC, потому что было важно отделить слой бизнес-логики от слоя протокола. Его преимущество в том, что заголовки ответов читаются однозначно для нас и для клиента. Например, если пришел ответ 500, то это значит это проблема на серверной стороне. Если ответ пришел 200, но внутри ответа содержится расшифровка JSON-RPC ошибки, значит эта ошибка в приложении, слой бизнес-логики. 

А вот в Rest API все работает не так. Если возникает ошибка, то может приходить заголовок с кодом 500 и нам будет непонятно: это сам сервер упал или ответ от API с ошибкой 500. То есть здесь смешивается логика транспорта и приложения.

Как работает клиентский API 

Авторизация и токен

В большинстве ��лучаев для доступа к API потребует токен. Получить его можно после авторизации, отправив запрос на специальный url: https://api.sweb.ru/notAuthorized/. Выглядит этот запрос вот так:

{
  "jsonrpc": "2.0",
  "method": "getToken",
  "params": {
    	"login": "<ваш логин>",
	"password": "<ваш пароль>"
  }
}

Сам токен будет содержаться в ответе в параметре result:

{
  "jsonrpc": "2.0",
  "id": "20220505104244.40FxsQ16Ff",
  "result": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

Запрос

Для доступа к API запрашивается отдельный url: https://api.sweb.ru/domains/. При этом запрос содержит следующие заголовки:

Content-Type: application/json; charset=utf-8
Accept: application/json
Authorization: Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Все запросы, кроме тех, которые адресованы к общедоступным методам, должны содержать токен авторизации Authorization: Bearer.

Тело запроса может иметь вид:

{
  "jsonrpc": "2.0",
  "method": "move",
  "params": {
    "domain": "vpstest.ru"
  },
  "id": "20183994338.43VSEkfGFh",
  "user": "xxxxxxxxx"
}

В этом запросе имеются следующие параметры:

  • jsonrpc — текущая версия JSON-RPC. Это обязательный параметр.

  • method — метод объекта Domains. Если он не передан, то система назначит метод объекта по умолчанию.

  • params — объект параметров метода (ключ элемента объекта - имя параметра).

  • id — уникальный идентификатор запроса. Если он в запросе не содержится, то id будет сформирован на стороне API.

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

Ответ

Если запрос к API отработан успешно, то система возвращает результат в виде JSON:

{
  "jsonrpc": "2.0",
  "id": "20183995523.MO4E9baKRr",
  "result": {
    "balance": {
      "real_balance": 500,
      "bonus_balance": 0
	}
  }
}

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

  • jsonrpc — текущая версия JSON-RPC;

  • version — текущая версия клиента;

  • result — результат, который возвращает вызванный метод;

  • id — уникальный идентификатор ответа, если был в запросе, то совпадает с ним.

Если при обработке запроса возникает ошибка, то форма ответа будет такой:

{
  "jsonrpc": "2.0",
  "version": "0.1",
  "id": "20183910121.UPNWsDxwmn",
  "error": {
    "code": -32601,
    "message": "Object not found"
  }
}

Расширенное сообщение о результатах работы метода

Некоторые методы, помимо успешного или неуспешного ответа, должны передать клиенту кастомизированное сообщение или данные. Для таких ответов применяется общий тип ExtendedResult. Выглядят такие ответы следующим образом:

{
  "jsonrpc": "2.0",
  "id": "20183995523.MO4E9baKRr",
  "result": {
    "extendedResult": {
      "code": 1,
      "message": "Заявка #180047823 принята в работу",
      "data": []
	}
  }
}
{
  "jsonrpc": "2.0",
  "id": "20183995523.MO4E9baKRr",
  "result": {
    "extendedResult": {
      "code": 0,
      "message": "Зона .child не поддерживается для регистрации",
      "data": []
	}
  }
}

Здесь code 1 означает успешное выполнение, а 0 ошибку. А поле data (объект дополнительных данных) может оставаться пустым.

К каким данным дает доступ API

Функции, к которым клиент может получить доступ после авторизации, разделены на несколько категорий в соответствии с бизнес-логикой: виртуальный хостинг, VPS, домены, партнерская программа, финансовый сервис. Внутри каждого раздела тоже есть иерархическая система из объектов, которые объединяют несколько функций — отдельных методов.

Реальный кейс использования API

Основные пользователи API — веб-мастера, которые управляют сразу несколькими аккаунтами разных клиентов. Для них это очень удобная фича. 

Если бы не было API, работа выглядела бы так: заходишь в панель управления одного клиента, выгружаешь данные (например, о балансе, доменах и SSL с приближающейся датой окончания регистрации), анализируешь их в таблице. А если нужно посмотреть метрики другого клиента или сравнить их, выходишь из предыдущего аккаунта и заходишь в следующий. И так несколько раз.

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

В нашем API используются разные методы для управления аккаунтами, подробно мы их описывали в документации. Один из популярных — https://api.sweb.ru/pay/ метод index, он дает информацию о балансе аккаунта, количестве доменных бонусов, за счет которых можно бесплатно продлевать и регистрировать домены, статус аккаунта и ожидаемую дату блокировки.

Пример запроса на url: https://api.sweb.ru/pay

{
  "jsonrpc":"2.0"
   "id":"715670275654947.SpiAFUtJpx"
   "method":"index"
   "params":{}
}

Пример ответа:

{
  "jsonrpc":"2.0"
  "id":"715670275654947.SpiAFUtJpx"
  "params":[
        {
          "balance":{
          "real_balance":30
          "bonus_balance":0
         }
        "auto_payment_enable":0
        "isAutopaymentEnable":1
        "domainBonuses":0
        "status":"active"
         "blockInfo":{
         "days_date":"07.02.2023"
         "days":0
         "days_word":"дней"
}
"blockedMoney":0
"deferment":{
"show":false
"value":0
}
"edgeDate":"2022-11-14"
       }
   ]
}

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

Для верифицированных клиентов доступны методы, с помощью которых можно создавать отдельные аккаунты под привлеченных клиентов — createOrderVh, checkLogin, createOrderVip и другие. 

Используя их, можно сделать форму регистрации, с помощью которой, не переходя на сайт SpaceWeb, можно зарегистрироваться на виртуальном хостинге или VPS. А созданный аккаунт будет автоматически подключен к партнерской программе. У конкурентов такого нет.

Планы по развитию API

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

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

А вы используете API при работе с хостинг-провайдерами? Каких методов вам не хватает?