Пользователь
0,0
рейтинг
17 июля 2015 в 19:07

Разработка → Автоматическая генерация API doc через Аннотации или как прийти к документированию API из песочницы

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

Откуда взялась идея? За последние несколько лет я сменил 3-4 компании, в которых мне доставалась мягко говоря лапша, толстенные контроллеры и минимум аннотаций. С чем именно связать отсутствие аннотаций — сказать сложно, возможно, это банальная лень или не знание того, что аннотации позволяют описывать больше, чем просто входящие параметры для классов/методов. А когда это начинает касаться API-контроллеров, то тут вообще весело, прикрутить FosRestBundle не проблема (я сейчас про более ленивый вариант, чем написание всего и вся самому ручками, хотя, как показывает практика — бандл проще).

Так вот, о чем это я? Прикрутили бандл, описали (в лучшем случае) через ParameterFetcher входные параметры и правила. Вроде бы все готово и работает, запросы бегают, ответы летают. Но как быть когда нужна документация на АПИ, какие варианты я только не видел. Самый убийственный был текстовые файлики в папке Resource/doc — но хоть служили куда надо. Уверен что для большенства кто будет читать это статью а не закроют ее сразу же, многое будет и так известно, но я все же опишу.

Приведу все примеры с ссылками на где брать и скринами как работает.

Итак, сам бандл — тут.

Далее идет спойлер с установкой NelimoApiDocBundle.

click
Ставится как и все остальное очень легко и просто через Composer:

        require nelmio/api-doc-bundle
    

Далее все еще проще его в AppKernel.php

        public function registerBundles()
        {
        //...
            return array(
                // ...
                new Nelmio\ApiDocBundle\NelmioApiDocBundle(),
            );
        }
    

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

        # app/config/routing.yml

        NelmioApiDocBundle:
            resource: "@NelmioApiDocBundle/Resources/config/routing.yml"
            prefix:   /api/doc
    


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

Итак, у нас есть простой метод:

click
        public function getIndexAction(ParamFetcher $paramFetcher)
        {
            $view = View::create();

            $result = [
                'status' => 'success',
                'data' => [
                    'someKey' => 'SomeValue'
                ]
            ];
            $view->setData(['result' => $result]);
            return $view;
        }
    


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

click
        /**
         * @Get(name="_additional_name_in_route", path="/")
         *
         * @Template(engine="twig", template="HabrApiDocBundle:Default:index.html.twig")
         *
         * @QueryParam(
         *      name="name",
         *      requirements="\w+",
         *      strict=true,
         *      nullable=false,
         *      description="Some description"
         * )
         *
         * @param ParamFetcher $paramFetcher
         * @return View
         */
        public function getIndexAction(ParamFetcher $paramFetcher)
        {
            $view = View::create();

            $result = [
                'status' => 'success',
                'data' => [
                    'someKey' => 'SomeValue'
                ]
            ];
            $view->setData(['result' => $result]);
            return $view;
        }
    


Теперь можно разобраться, что ж мы там написали такого:
  1. @Get — самый простой и быстрый способ переопределить урл роута для методов, которые отвечают только на 1 тип запроса (REST Full). Это даст нам возможность сделать postIndexAction deleteIndexAction — ну и так далее, суть поймана.
  2. @Templating — использовать или нет, тут уже по желанию. Я предпочту использовать, например, для стандартизации ответов.
  3. @QueryParam — первая плюшка, использование @QueryParam или @RequestParam соответственно позволит вам управлять входными данными. Например, у нас есть поле name, которое НЕ может быть пустым, должно соответствовать патерну \w+ и не менее главное — это Description, в котором можно написать, на кой оно надо взагали.

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

* @ApiDoc(
*      statusCodes={
*          200="Returned when successful",
*          403="Returned when the user is not authorized to say hello",
*          404={
*              "Returned when the user is not found",
*              "Returned when something else is not found"
*                },
*            },
*            description = "Add here some description for this method",
*           requirements = {
*                {"name"="name", "datatype"="string", "requirements"="\w+", "description" = "description for this parameter"}
*            },
*            cache=false,
*            tags = {
*                "stable" = "green",
*                "deprecated" = "#ff0000"
*            },
*            deprecated = true,
*            section = "First section"
*       )

Начну описывать по порядку:
  1. @ApiDoc() — естественно мы в начале укажем к испольлзованию сам бандл,
  2. statusCodes — назначение данного параметра видно из его названия, но он в итоге очень красиво выглядит в документации, расскажет нам, что и в каких случаях мы можем получить в качестве ответа.
  3. description — ну тоже как бы пришел капитан, с той лишь разницей, что теперь его будет видно в документации.
  4. requirements — сюда пишется массив Query/Request параметров с описанием их требований, что так же будет отображено в документации.
  5. tags — теги, это просто нечто. Когда можно на каждый метод поставить какой нибудь свой тег.
  6. deprecated — тоже приятная фича, когда можно помечать методы, которые планируется больше не использовать.
  7. section — одно из самых полезных дополнений, позволяет объединять в группы методы, даже если они находятся в разных классах.

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

ScreenShot
Результат сгенерированной доки


P.S> Поправил ссылку на скрин
Финкель Морис @Morisf
карма
0,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

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

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

  • +1
    Swagger делает то же самое.
    • 0
      Да, он делает тоже самое — НО!!!
      Это JS и значи работы будет еще больше.
      Nelmio — это готовый бандл который просто прочтет нужные каменты — а значит будет быстрее
      + его можно заекстендить и расширить /
      • 0
        • 0
          Значит не на тот свагер зашел )))
          • 0
            сваггер он один, есть просто разные реализации парсеров форматов и прочей белеберды.
  • –1
    Я просто оставлю это здесь:
    @api
  • –2
    Я не спорю что есть что то делающее то же самое
    Но например зайдя в api функционал, скуден как по мне нет нормальной документации
  • 0
    И вот так легким движением руки делается дублирование — указанное в @ApiDoc с вероятностью 146% будет расходиться с тем, что на самом деле происходит в коде. Поэтому для «своих» эта документация вредна — надежнее посмотреть в код, а для «чужих» — источник боли, страдания и унижения.
    • 0
      147%, чтобы символичнее было
    • 0
      Если так думать обо всем то смысл тогда вообще делать документацию и писать какой либо код
  • 0
    Главная проблема этих штук — генерация документации по API постфактум. То есть когда код уже написан. Это вроде здорово, но не для всех подходит.

    Я в последнее время пришел к такому флоу:

    1) мобильщики/фронтэндщики и бэкэндщики содятся обсуждать фичу и API которое им надо предоставить. Все решения записываются в доку в формате api blueprint (MSON — киллер фича, конкурентов которой я пока ненашел).
    2) Мобильщики фронтэндеры садятся писать свою часть функционала и на время пока апишка не готова могут поднять mock-сервер сгенерированнй на основе документации.
    3) бэкэндер может реализовать апишку и забрать схему респонсов скажем из документации (к сожалению парсера для php пока нет, приходится конвертить markdown в json средствами node.js и затем уже юзать, но это пока я парсер не допишу), и валидировать скажем в тестах на предмет соотвествия документации. Ну и опять же можно вместо прописывания урлов брать все из документации (по названию действий) и тогда можно тестами покрыть и функционал и соответствие документации которую ожидают от вас мобильщики/фронтэндеры.

    Профит.
    • 0
      Если гоборить о «interface first», то мы сразу создаем эндпойнты и апи-модели, так и документацию можем сразу сгенерировать, и сервер сразу поднять, при необходимости даже модели через рефлекшн заполнять. Плюс, что спорно конечно, но мне ужасно не нравится когда api документация и код разделены. Ну и в конце концов, так час-другой обсуждений и api frontend готов.
      • 0
        когда api документация и код разделены

        А мне нравится, все в markdown, можно по дифам коммитов смотреть что поменялось в апишке за такой-то период. А если использовать эту самую документацию в функциональных тестах у нас нет проблем с тем что бы следить за тем что бы документация соотвествовала истене.
      • 0
        Еще добавлю…

        В целом же api blueprint + mson как по мне поддерживать проще и удобнее чем гору аннотаций в контролерах. Да и читается оно приятно, и на сервак не попадет (апишки в моем случае приватные, документация так же, так что так удобнее и проще).

        А ну и еще — я не использую таких вещей как JmsSerializer и прочие fos rest bundles (точнее последний я использую но только что бы не писать свой exception controller и прочие мелочи жизни), а в качестве DTO используются динамические штуки (массивы, stdClass, ect). Так что при использовании NelmioApiDocBundle дублирования происходит сильно больше чем при использовании MSON.

        Опять же повторюсь — это чисто мой случай.
        • 0
          Главное что бы документация была! и без ошибок) Во всех подходах есть и минусы и плюсы. В вашем варианте мне особенно понравилась генерация интеграционных тестов апи. А валидацию входных моделей делаете на освное того же json, или?
          • 0
            Пока нет, ибо информации из документации не хватает что бы полноценно организовать валидацию (там только типы данных и обязательное ли поле), можно в теории организовать разбор запроса в DTO но это много кода.
    • 0
      Ну скажем у меня код пишется постфакту после планирования того зачем этот метод вообще нужен.
      Когда все решено то я описываю что он принимает и что отдает а потом уже пишу код который в итоге не расходится с документацией
      • 0
        Ну а у меня документация пишется вместе с планированием. То есть мы сначала обсуждаем фичу, потом пишем документацию. Сейчас к документации по апи планирую добавить еще фичаспеки.

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

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