Пользователь
0,0
рейтинг
2 июля 2014 в 19:01

Разработка → Универсальный web-GUI для произвольных RESTful сервисов из песочницы

Во многих компаниях, как и моей, есть много проектов и продуктов. И у продуктов бывают веб-интерфейсы, чтобы этими продуктами как-то манипулировать. В нашем случае это простенькие RESTful веб-сервисы, а поверх них ещё более простенькие веб-странички с формочками и кнопочками. Все эти веб-странички до того похожи друг на друга, что возникла мысль написать унифицированный продукт, который бы спрашивал сервер о поддерживаемых сервисах, и получал бы полное описание параметров к этим сервисам, так чтобы можно было нарисовать те самые простенькие формочки. То есть, веб-сервисы должны описывать себя, достаточно исчерпывающе, чтобы наш клиент мог построить GUI для них, и ничего не надо было бы делать руками. Как раз такая картинка гуглится по запросу «REST»:



Сходу нашлась такая технология, как WADL — Web Application Description Language. Но это XML, с ним не хотелось возиться в javascript, python и perl. Был выбран его более легковесный JSON-аналог — JSDL — JSON Service Description Language.

Требования к сервисам



Оказалось, что в JSDL (как и в WADL) не помещается вся информация, нужная для формирования полноценных формочек, и ряд дополнительных соглашений с веб-сервисами должны быть установлены:
  • Для начала нужно получить список сервисов, и как они называются. Понятие сервиса (в терминах JSDL или WADL) здесь соответствует манипулируемой сущности (ресурсу в терминах REST). У сервиса есть набор операций, которые и описывает JSDL. Поэтому сервисов может быть (и обычно есть) несколько, и они могут быть объединены в группы для удобства отображения. Поэтому первым делом наш клиент запрашивает URL GET /service_groups и получает JSON вида
    [
        {
            "description": "Manipulate resource1",
            "services": ["resource1"]
        },
        {
            "description": "Manipulate resource2 and its statistics",
            "services": ["resource2", "resource2_stat"]
        }
    ]
    

  • Далее нужно получить JSDL для всех этих сервисов. Можно сделать по запросу для каждого сервиса, но для удобства было так же реализовано получение всех объектов одним запросом GET /jsdls. Получаем JSON вида
    [["resource1", {...object...}], ["resource2", {...object...}], ["resource2_stat", {...object...}]]
    

    где object — JSDL-описание сервиса (операции, их параметры и т.д.).
  • Казалось бы, всё. Но не всё. Наш клиент — просто html-файлик, который может быть доступен с произвольного домена, либо вообще без домена, если мы натравим броузер прямо на него. Далее мы вводим в формочку корневой URL нашего RESTful API, и туда делаются запросы, являющиеся cross-domain. И мы неизбежно сталкиваемся с безопасностью. Если бы мы могли ограничиться GET-запросами, можно было бы использовать JSONP, но и это наложило бы дополнительные требования собственно на RESTful API.
    Вместо этого сервер должен поддерживать OPTIONS метод, про который мало кто знает, и который практически не используется, хотя броузер неявно делает такой запрос при кросс-доменных ajax-вызовах. В RFC про этот метод написано по-моему довольно расплывчато. На практике сервер должен выдавать на этот запрос заголовок Allow:
    Allow: HEAD,GET,PUT,POST,DELETE,OPTIONS
    Кроме того, для каждого запроса нужно слать заголовки вида:
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Headers: content-type,X-Requested-With
    Access-Control-Allow-Methods: HEAD,GET,PUT,POST,DELETE,OPTIONS
    Тогда броузер доволен.


Валидируем JSDL


Наш клиент валидирует пришедшие от сервера JSON-объекты в соответствии со схемой JSDL, описанной с помощью JSON Schema. Мы просто взяли JSDL-схему, метасхему JSONa, и первый валидатор из предложенных на сайте, вот этот. Метасхема нужна, так как параметр в JSDL является схемой и описывается метасхемой, которую мы немного расширили, как указано ниже. Надеюсь, понятно (мне лично не всегда).

Расширяем JSDL


К сожалению, средствами стандартного JSDL всё равно не получается полностью выразить наши формочки.
Схеме параметра не хватало следующих выразительных средств:
  1. Параметру нужно имя. Его нужно задать.
  2. Параметр передаётся серверу разными способами:
    • В URL, после ресурса: api.com/resource1/paramvalue1/paramvalue2
    • В строке запроса (если GET) или в теле запроса: api.com/resource1?param1=value1&param2=value2
    • В теле запроса, единым куском, не образуя key-value пары. Специфичный случай, но встречается
  3. Для каких параметров типа string нужно рисовать textarea, а каким достаточно text input?


Для решения этих проблем схему JSDL-описания параметра (ту самую метасхему) пришлось расширить следующим образом:
//добавим атрибут name
ParamJSONSchema["properties"]["name"] = {
"type": "string"
}
//добавим атрибут passing - как передавать значение
ParamJSONSchema["properties"]["passing"] = {
    "type": "string",
    "default": "keyvalue",
    "enum": ["keyvalue", "positional", "raw"]
}
//добавим в типы параметра multistring для случая textarea
ParamJSONSchema["definitions"]["simpleTypes"]["enum"].push("multistring");


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

Сам проект лежит на гитхабе и может быть использован для реализации единого веб-интерфейса к сервисам в вашей компании, например.
Андрей @nifigase
карма
2,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (10)

  • +9
    есть же swagger helloreverb.com/developers/swagger
    • 0
      На сколько я понял, автор ставил другие задачи. Свагер создает UI для разработчиков, автор же предлагает решение, которое используют непосредсвтенно кастомеры (пользователи).
      • +1
        Swagger это прежде всего спецификация, которую в частности можно использовать и для построения ui клиента.
        • 0
          Ну и js-библиотеки, строящие ui клиента, там тоже есть. Да, можно использовать, этот продукт мне не попадался раньше. Его протокол, конечно, не основан на JSDL или на чём-то ещё. А хотелось сохранить хоть какую-то близость хоть к какому-то стандарту.
    • 0
      Swagger крут, но только если создаётся новоё API. Swagger не получилось использовать для существующего API, т.к. в самой спецификации уже есть предположения о структуре запросов-ответов.
      • 0
        Это что за «предположения» такие?
  • +2
    Похожий принцип построения UI используется в metawidget.org/. Он может строить формы на основе лишь исходных данных. Поддерживаются примитивные типы данных, списки и вложенные структуры. При необходимости представление обогащается, так же, путем добавления описания типов данных (например в виде JSON Schema). Хотя это сильно кросс-платформенная штука (я не знаю больше половины названий заявленных технологий :) ), но конкретно для JavaScript есть вполне годная реализация фронтенда под AngularJS и Bootstrap3.
  • 0
    а как-нибудь можно туда прикрутить acl?
  • 0
    Jsonary еще посмотрите. Вот пример.
    Поддерживает отображение/редактирование со схемой или без. Кастомные контролы и плагины к нему не пробовал.
    Исходников немного. В моем небольшом проекте работает хорошо. Редактирования массивов объектов я пока нигде больше не видел, а jsonary умеет (хоть из слегка неудобно).
  • 0
    Проект интересный, только выглядит заброшенным. Есть какое-то демо получаемого интерфейса?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.