пʼятницю, 12 жовтня 2018 р.

REST is not RPC

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

Индустрия парализована. Парализована банально не правильным пониманием концепции и эхолалией. Одна хорошая идея в неправильной трактовке может принести много вреда. Всё было довольно просто, хотелось оформить всё единообразно, а получилось… безобразно! Удивительно, как крайне компетентные, уважаемые люди и те попадают под власть красивых сокращений.

Часто ли тебе говорили, “посмотри на REST API сервиса Х” или “у нас реализовано REST API”, ты приходил и видел, что на самом деле никакого REST API нет и в помине, обычный HTTP API, хаотичным образом разбросанные HTTP глаголы в купе с уродливыми URL. Со мной такое случалось последние несколько лет и если раньше я не особо обращал внимание на это, то спустя время ошибочная трактовка REST начала бросаться в глаза. Позволь объяснить, что я понимаю под “правильным” пониманием REST. С одной стороны, можно трактовать REST по Филдингу. Тогда, мы обязаны обеспечить следующие условия:

  1. Клиент-серверная модель взаимодействия;
  2. Stateless;
  3. Кеширование;
  4. Единообразие интерфейса;
  5. Слоистая архитектура со стороны сервера;
  6. Код по требованию, расширяющий функциональность клиента.

Хм, многовато сложности, давай упростим и предположим, что когда говорят о REST API, то в идеале имеют ввиду соблюдение условия “4 Единообразие интерфейса”. Кстати, окинь взглядом этот список еще раз и подумай, как близко твоя система, которую ты в данный момент разрабатываешь. Я могу честно отметить, что ни в одной системе мне не приходилось реализовывать пункт 6, хотя возможно под него подойдет загрузка JS из CDN, тогда всё в порядке.

Можно выделить несколько важных пунктов о единообразии интерфейса, т.е. о создании REST API:

  1. Явно выделенные ресурсы (например, пользователь). Метод HTTP API, который называется “/createUser” очевидно нельзя называть частью REST API - это отличный пример реализации RPC поверх HTTP. Думая о пользователях, как о ресурсах мы получаем uri, который заканчивается на “/users”. Обрати внимание, “users” это множественное число. Из собственной практики могу заключить, что такой вариант воспринимается лучше, это выглядит более естественным при запросе множества пользователей. К сожалению, не во всех проектах мне удается этому следовать, но помни, главное - единообразие. Если в большей части системы для получения множества ресурсов уже используется единственное число, лучше так и оставить - меньше будет ошибок у пользователей твоего API.

  2. HTTP глаголы это не просто глаголы. Во первых, REST это не обязательно про HTTP. Вот так, теперь живи с этим. Суть HTTP глаголов в REST - помочь тебе манипулировать ресурсами через их представления. Кроме очевидной логики в названиях HTTP глаголов POST, PUT. DELETE, GET, есть один важный момент который мы часто упускаем. Имя его - идемпотентность. Тебя когда-нибудь спрашивали на собеседовании “почему для update используют глагол PUT”? Обычно, я в таких ситуациях отвечал “ну, логично же” и с этим никто не спорил, хотя есть и другое объяснение. Основное различие между PUT и POST это идемпотентность. Это обозначает, что PUT, как идемпотентный метод, можно запустить многократно с одинаковым результатом, а вот POST нельзя. Предположим, что у вас есть реализация “POST /users” и “PUT /users”, естественно, в теле запроса находится user. Если запустить первый метод 20 раз, то будет создано 20 пользователей (при условии отсутствия уникальных индексов), а если запустить 20 раз второй запрос, то результирующее состояние будет тем же, что и при первом вызове. Идемпотентность это важное свойство, которое следует соблюдать для избежания несогласованой трактовки работы твоего API.

  3. Гипермедиа. Пункт, который на моей практике не так часто соблюдают, возможно из-за отсутствия единого стандарта, но скорее из-за лени и нежелания это поддерживать. Сама по себе концепция проста, действия определяются на сервере, т.е. ссылки которые могут повлиять на состояние системы. Кроме простых точек входа в систему могут быть и более сложные действия, которые необходимо определять. Однако, складывается впечатление, что это несколько вырожденный пункт, который сегодня перекрывается возможностями автоматически генерируемой документации.

  4. Content-Type & Accept заголовки помогают понять как же именно обрабатывать запрос, что находится в его теле и какой формат ожидается в ответ. Когда у вас один веб клиент, этот пункт кажется излишним, но на практике, это хорошее ребро жесткости для API, которое позволяет получать именно то, что ожидается в ответ.

Итак, мы разобрались, что RESTful системы делает вовсе не каждый. Достаточно завести базу в которой ты будешь хранить состояние сессии и всё, по условию Stateless ты уже не проходишь. Окей, но что нам стоит построить хотя бы RESTful API? Дисциплина помогает писать в REST стиле конечные точки: правильные url, корректное использование глаголов и даже заголовки приведены в порядок. Осталось гипермедиа… Один шаг и у нас по настоящему RESTful API. Что-то внутри мне подсказывает, что это непреодолимый в общем случае шаг, что это достаточно много трудозатрат ради минимального выхлопа, при том, что закрыть проблему, которую закрывает hypermedia можно и с помощью swagger, не так гибко, но всё же.

В сухом остатке, по моему субъективному опыту мало кто пишет настоящий REST API, однако, мы можем довольно просто приблизиться к нему. Но зачем обманывать себя? Почему недостаточно сказать “посмотри на сервис Х у них HTTP API” или “у нас HTTP API”? Видимо, люди надеятся спрятать за словом, которое должно характеризовать надежные, производительные и масштабируемые системы, то, что они делают. Получается, даже приведение интерфейса к единообразному виду делает далеко не каждый, зато мы спешим повесить лейбл “RESTful” на свой RPC.

HyperComments for Blogger

comments powered by HyperComments