Первая часть статьи была посвящена вопросу реализации клиентского REST-приложения под Android. Корректная реализация требует использования одного из паттернов, предложенных Virgil Dobjanschi. Существует ряд подходящих библиотек, способных облегчить эту задачу. Наиболее интересной из них мне показалась RoboSpice.
Прежде чем переходить к разработке тестового проекта на RoboSpice, необходимо определиться с API для тестового проекта. Разумеется, можно взять одно из готовых API - благо их сейчас сотни. Однако гораздо интереснее написать свое, тем более, что это нетривиальный процесс. Рассмотрим, как пишутся REST API, какие инструменты для этого существуют, и попробуем реализовать простейшее тестовое API.
Принципы разработки REST API
Apigee
Если вы собираетесь разрабатывать REST API самостоятельно, то первым делом загляните на сайт apigee.com. Здесь собрана масса полезной информации на эту тему. В первую очередь - книги:- Web API Design. Crafting Interfaces that Developers Love (2011, pdf) by Brian Mulloy (вебинар). Кратко и четко расписаны основные принципы разработки API: как именовать ресурсы и действия, паджинация, как возвращать ошибки, поддерживать несколько форматов представления ресурсов, как реализовывать поиск и т.д и т.п. На хабре есть хорошая выжимка на русском.
- API Facade Pattern: A Simple Interface to a Complex System (2012, pdf) by Brian Mulloy (вебинар);
- APIs: A Strategy Guide. Creating Channels with Application Programming Interfaces (2013) by Daniel Jacobson, Greg Brail, Dan Woods;
- OAuth: The Big Picture (2011, pdf) by Greg Brail & Sam Ramji, (вебинар);
- и другие полезные книжки.
Другие ресурсы
- Книга RESTful Web APIs (2013) by Leonard Richardson, Mike Amundsen, Sam Ruby.
- REST+JSON API Design - Best Practices for Developers: slides, video
- Best Practices for Designing a Pragmatic RESTful API by Vinay Sahni
- REST and JSON API guidelines and best practices by Niels Krijger - удобная шпаргалка, краткая компиляция из других источников.
- HTTP: The Definitive Guide by David Gourley, Brian Totty, Marjorie Sayer, Anshu Aggarwal, Sailu Reddy. В книге детально рассмотрен HTTP протокол, на котором в большинстве случаев реализуются REST API. Так же HTTP посвящена отдельная глава "HTTP for APIs" в книге "RESTful Web APIs".
Тестовый пример: набор книг
Рассмотрим простую задачу - нужно разработать REST API для набора книг. Ресурс, для простоты, будет только один - книга (book). Что нам требуется от API:- [1] получить полный список книг (с пагинацией).
- [2] получить информацию о книге с заданной степенью детализации;
- [3] добавить книгу в набор;
- [4] удалить книгу из набора;
- [5] редактировать информацию о книге, например, уточнить ФИО автора;
- [6] провести поиск книг по автору или названию (с пагинацией);
Первая попытка описания API: список URL в текстовом файле
В первом приближении, наше API можно записать в виде простого списка HTTP-запросов, которые клиент может посылать на сервер. Учитывая рекомендации из книги "Web API Design. Crafting Interfaces that Developers Love", получаем такие запросы://[1] получить полный список книг GET /v1/books GET /v1/books?limit=10&offset=30 //поиск с пагинацией; получаем максимум 10 результатов, начиная с 30-ой позиции //[2] получить информацию о книге GET /v1/books/[book_id] GET /v1/books/[book_id]?fields=title,author,description //явно указываем, какие поля нам нужны //[3] добавить книгу в набор POST /v1/books //[4] удалить книгу из набора DELETE /v1/books/[book_id] //[5] редактировать информацию о книге PATCH /v1/books/[book_id] //заменяем только выбранные поля переданными данными //[6] провести поиск книг по автору, по названию GET /v1/books?[book_id]?author=John //поиск по автору; по умолчанию выводится первые 10 результатов GET /v1/books?[book_id]?author=John&limit=25&offset=50 //поиск по автору с пагинацией GET /v1/books?[book_id]?title=hero&?fields=title,author //поиск по названию, явно указываем, какие поля нам нужныДля простоты считаем, что наши клиенты поддерживают все необходимые HTTP-методы. На практике, некоторые клиенты могут не поддрживать PUT, DELETE и, тем более, PATCH, так что могут потребоваться альтернативные варианты API, например, вместо
PUT /v1/books/[book_id]
- POST /v1/books/[book_id].patch
. Тег v1
задает версию нашего API.
Договоримся, что для формат представления ресурсов можно задавать следующим образом:
GET /v1/books //json по умолчанию GET /v1/books.xml //получить список книг в xml GET /v1/books/[book_id].proto //получить информацию о книге в формате protobufferТеперь зафиксируем способ обработки ошибок. Будем считать, что в случае успеха сервер возвращает код
200 - OK
, в случае ошибки один из следующих кодов: 400 - Bad Request, 500 - Internal Server Error
. Для наших целей вполне достаточно, хотя можно задействовать и другие коды.
В случае ошибки сервер обязан вернуть сообщение с расшифровкой ошибки в формате:
{"code" : 401, "message": "book wasn't found"}Осталось описать формат представления ресурса и списков ресурсов.
- [1] получить полный список книг
GET /books Response 200 OK {"books": [{"book":{ "id":"1234", "title": "Война и мир", "author": "Л.Н. Толстой", "released": "1868"}} , {"book":{ "id":"1235", "title":"Господин Ау", "author", "Э. Успенский", "released" : "1980"}}] "_metadata": [{"totalCount":250,"limit":10,"offset":0}] "_links" : [ {"rel": "next", "href": "/v1/books?offset=30&limit=10}, {"rel": "prev", "href": "/v1/books?offset=10}, {"rel": "first","href": "/v1/books?offset=0}, {"rel": "last", "href": "/v1/books?offset=240} ]}
- [2] получить информацию о книге
GET /books/1234 Response 200 OK {"book":{ "id":"1234", "title": "Война и мир", "author": "Л.Н. Толстой", "released": "1868" }}
- [3] добавить книгу в набор
POST /v1/books title="Желтый туман"&author="Александр Волков" Response 200 OK {"book":{ "id":"1236", "title": "Желтый туман", "author": "Александр Волков", "released" : ""}}
- [4] удалить книгу из набора
DELETE /v1/books/[book_id] Response 200 OK
- [5] редактировать информацию о книге
PATCH /v1/books/1236 released="1970" Response 200 OK {"book":{ "id":"1236", "title": "Желтый туман", "author": "Александр Волков", "released": "1970"}}
- [6] провести поиск книг по автору, по названию - аналогично [1]
Инструменты для описания API
Вариант описания API в виде текстового файла вполне жизнеспособен, но далеко не идеален. Основная проблема в том, что в этом случае реализация и документация API неминуемо разойдутся. Решений несколько:- [1] Описать API, воспользовавшись одним из специальных языков. Далее, на основе описания, автоматически генерировать документацию и код реализации, проверять корректность и полноту реализацию API и т.д.
- [2] Описать API в коде сервера, генерировать актуальную документацию на основе кода.
- [3] Воспользоваться подходом Hypermedia as the Engine of Application State (HATEOAS) и вообще обойтись без описания API. Такой подход подразумевает, что клиент изначально знает лишь адрес сервера, и узнает все ссылки для доступа к конкретным ресурсам в процессе навигации. Для его реализации можно использовать обобщенные языки описания API типа HAL, Siren. В данной статье подход HATEOAS рассматриваться не будет.
- apiary.io. Бесплатный сервис, который позволяет разрабатывать REST API на языке API Blueprint Language. На основе такого API можно:
- генерировать документацию;
- проверить реализацию на соответствие API; проверка выполняется следующим образом: сервер Apiari используется как отладочный прокси-сервер, который пропускает через себя весь трафик к серверу и от сервера, сравнивает запросы и результаты с API и сообщает о найденных ошибках.
- сервис может работать как сервер-заглушка (Server Mock), так что с API можно будет попробовать работать сразу, не реализуя реальную серверную часть;
- сервис поддерживает отладку запросов (в разделе "Documentation" возле каждого запроса есть кнопочка Try it).
- сервис плотно интегрирован с GitHub.
- http://www.apihub.com/. Альтернативный вариант, использующий для описания REST API язык RESTful API Modeling Language (RAML). Сервис платный, с возможностью бесплатно разработать одно API.
- http://enunciate.codehaus.org - Java + JAX-RS аннотации - для тех, кто разрабатывает сервер на основе JAX-RS.
- Самописные велосипеды;
- и т.д.
Вторая попытка описания API: Apiary.io
Воспользуемся сервисом apiari.io, и перепишем наше API, используя документацию по API Blueprint Language и образцы готовых API (1, 2). Результат можно посмотреть на GitHub и на Apiary. А вот результаты работы Mock-сервера Apiary:- http://samplebookssetrestapi.apiary.io/v1/books/1236
- http://samplebookssetrestapi.apiary.io/v1/books
## Book [/v1/books/{id}{?fields}]и действия к нему
### Retrieve a Single Book [GET] ... ### Edit a Book [PATCH] ... ### Delete a Book [DELETE] ...Параметр
fields
может использоваться только с GET, а в PATCN и DELETE его быть не может. Набор параметров я задаю отдельно для каждого действия. Тем не менее, в документации для Edit и Delete параметр fields
присутствует в URL, что несколько сбивает с толку. Причем ниже, в списке возможных параметров, он отсутствует.
Несколько неудобно, хотя не слишком критично.
Выводы
- Прежде чем разрабатывать собственное REST API, имеет смысл ознакомиться как минимум с Web API Design. Crafting Interfaces that Developers Love (pdf), чтобы не наделать грубых ошибок.
- Сервис apiary.io - весьма функционален. Разрабатывать REST API на нем гораздо удобнее, чем в просто фиксировать список URL в текстовом файле. Возможность проверки реализации на соответствие спецификации и Mock-сервер стоят того, чтобы потратить время на изучение API Blueprint Language.
Комментариев нет:
Отправить комментарий