Статьи

Rest API Best (?) Перезагружены

Последние полтора года я участвовал в 2-3 проектах, в которых представлен большой набор API для отдыха для «внешнего» использования. Я вернусь позже и объясню, почему слово  внешнее  — это внутренние кавычки. В течение этого периода нам приходилось несколько раз разрабатывать, перепроектировать и реструктурировать эти API. В этой записи блога изложены мои личные мысли о некоторых лучших (?) Методах, которые помогут вам при работе с Rest API

Дизайн хорошо, дизайн рано

Внедрение услуг отдыха — очень тривиальная задача для многих языков. Это означает, что независимо от того, какую базовую среду вы выберете, с минимальной конфигурацией и несколькими строками кода вы сможете получить менее чем за час работающую службу Rest. Хотя это действительно удобно, особенно для неопытных людей, оно может очень быстро привести вас к некачественному дизайну API. Поэтому, прежде чем начать писать код, отойдите на минуту (или больше) и подумайте. Попробуйте разработать свой API. Потратьте достаточно времени, чтобы понять сферу деятельности и выяснить, что нужно клиентам API из вашей системы. Например, если ваша система представляет собой базу данных для сборщиков монет, решите, хотите ли вы разрешить клиентам добавлять новые монеты или просто получать существующие данные.Какие запросы нужны? Как вы будете обрабатывать запросы, которые будут получать большой объем данных? Ответы на эти вопросы довольно рано помогут вам разработать более реалистичный API. 

Имена и методы

Существует много дискуссий о наименовании ресурсов и о том, как они должны быть организованы. Опять же, основываясь на моем опыте, я предлагаю три простых правила.

  1. Используйте только существительные. Например, если вы хотите предоставить сервис для поиска в базе данных монет, не называйте конечную точку следующим образом: / searchCoins или / findCoins или / getAllCoins и т. Д. Достаточно простого ресурса / coins, поэтому, когда клиенты отправляют GET-запрос того ресурса, который они ожидают чтобы получить список всех доступных монет. Точно так же, если вы хотите добавить новую монету в базу данных, избегайте использования таких имен, как: / addCoin или / saveCoin или / insertCointToDatabase. Вы можете просто использовать имеющийся у вас ресурс для получения монет и вместо запроса GET просто отправить запрос POST или PUT для обновления монеты.
  2. Как насчет получения одной монеты. Рекомендуется использовать конечную точку, предоставляющую параметр ресурса, так что, например, если клиенты хотят получить монету с идентификатором 20, то достаточно запроса GET для / coins / 20. Давайте посмотрим еще один более сложный случай. Допустим, вы хотите разрешить клиентам добавлять изображения для каждой монеты. Быстрое, но грязное решение было бы: / addCoinImage или / addNewImageToCoin и т. Д. Немного лучший подход будет выглядеть как / coins / addImage, но, как я уже сказал, просто отбрасывайте любые глаголы. Таким образом, мы можем воспользоваться ресурсом, который мы предоставили для получения данных конкретной монеты, и позволить клиентам добавить изображение, отправив запрос POST в эту конечную точку: / coins / 20 / images. Все идет нормально. Но у каждой розы есть свой шип. Предположим, мы хотим разрешить некоторым суперпользователям удалять монеты из системы.Исходя из того, что мы обсуждали до сих пор, будет достаточно простого вызова DELETE для ресурса / coins / {id}. Но можете ли вы представить, насколько проблематичным будет такой подход, если {id} — это просто порядковый номер таблицы «COINS»? Кто-то может начать отправлять запросы DELETE на этот ресурс, увеличивая каждый раз идентификатор, в результате чего наша система в конечном итоге потеряет все данные. Моя точка зрения заключается в том, что использование идентификаторов в качестве параметров ресурса хорошо, если эти идентификаторы невозможно или слишком сложно угадать. Поэтому, если вы идентифицируете объект с помощью серийного номера, просто забудьте эту реализацию. Я предлагаю вместо передачи идентификатора монеты в качестве параметра ресурса отправлять запрос DELETE ресурсу / coins и включать в тело запроса (скажем, в виде документа json) достаточно параметров, чтобы однозначно идентифицировать объект сущности, который вы хотите удалять.
  3. Используйте доменные имена как можно больше. Если в вашем домене есть объект сборщиков монет, то при разработке API просто используйте сборщики  слов  вместо  пользователей  или  учетных записей  . Старайтесь избегать слишком общих имен, которые ничего не значат или противоречат клиентам. Следуйте тем же принципам и для параметров запроса. Также настоятельно рекомендуется, чтобы имена параметров запроса были как можно более короткими, но все же значимыми. Например, если вы хотите запросить монеты, выпущенные в определенный год, очень хорошим выбором для параметра запроса может быть:  issueYear  где следующие имена слишком велики или могут запутать клиентов:  year  (не ясно),  yearOfFirstIssue (бесполезная дополнительная информация) 

Обработка ошибок и ответы

Имея дело с ошибками и ответами, мой опыт показал, что клиентам гораздо легче ожидать одинаковую структуру ответов json при каждой отправке запроса, независимо от того, успешен ли результат или нет. Например, предположим, что вы хотите добавить новую монету, отправив запрос POST на ресурс / монеты. Успешный ответ может содержать следующий документ JSON

{     "meta":{
      "code":200
   },
   "data":{      "coinId":"a7sad-123kk-223"   }
}

и ответ об ошибке что-то вроде этого:

{     "meta":{
      "code":60001,      "error":"Can not add coin",      "info":"Missing one ore more required fields"
   },
   "data":{   }
}

Обратите внимание, что для обоих возможных результатов (ошибка, успех) документ ответа json имеет одинаковую базовую структуру. Есть два основных элемента: мета и данные. Первая содержит информацию о результате и в случае ошибки включает в себя конкретный код ошибки, сообщение («ошибка»), описывающее, что пошло не так, и более подробное объяснение («причина») основной причины сбоя. Последний (опционально) содержит любые данные, возвращаемые службой. Например, в приведенном выше примере, когда добавление новой монеты прошло успешно, сервис возвращает уникальный автоматически сгенерированный идентификатор для этой монеты. В случае ошибки обычно этот элемент должен быть пустым. Преимущество этого подхода заключается в том, что клиенты всегда ожидают стандартного возврата для всех служб одного и того же API. Кроме того, мы можем передавать дополнительную информацию, когда что-то не завершено успешно.Клиенты могут использовать (по крайней мере) атрибут error,  чтобы показать его в виде сообщения конечному пользователю и использовать  атрибут info  для регистрации. В качестве альтернативы они могут обрабатывать ответы на основе кода ошибки, если они знают значение каждого числа. Помните, что эти коды ошибок не являются http-статусами. Вы все равно должны возвращать правильный статус http при каждом запросе (т.е. 400, 401 и т. Д.)

Прежде чем мы обсудим подтему документации, я хотел бы отметить еще одну важную вещь, которую вы должны иметь в виду. Предположим, что мы не разрешаем удаление монет из монет, но клиент пытается отправить запрос DELETE на ресурс / coins / {id}. Обычно он возвращает ошибку http 405 из веб-контейнера. Я считаю очень полезным отфильтровать все эти ответы и снова вернуть тот же самый документ json. Таким образом, в случае вышеуказанной ошибки мы могли бы вернуть  

{     "meta":{
      "code":405,      "error":"Method not allowed for the /coins/{id} resource",      "info":"Method DELETE is not allowed for that resource. Available methods : GET, POST, OPTIONS"
   },
   "data":{   }
}

Не намного ли лучше? Ответ содержит ту же информацию (состояние ошибки http 405), но дополнительно информирует клиента о доступных методах для этого ресурса. 🙂

Документация

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

У меня еще есть пара практических советов, особенно по поводу управления версиями API и безопасности, но я думаю, что они оба могут подойти к другому сообщению в блоге 🙂

Если вам понравилась эта статья, не забудьте поделиться ей и взглянуть на мою будущую книгу: «Искусство садоводства программного обеспечения»