Полноценный REST API для перфекционистов за 5 минут

от автора

Привет Хабр! Меня зовут Владимир, мне 28 лет и я наркоман наркоман. Мой наркотик – простота. На простоту я подсел из-за своего перфекционизма, которым меня наградили при рождении. Врачи говорят, что это взаимосвязано, мол перфекционизм — это стремление к совершенству, а простота позволяет подобраться к этому мифическому совершенству. Чем проще решение, тем меньше ошибок можно допустить, вот я и подсел. Я не стал с ними спорить и вместо того, что бы искать виновников моей истории, решил с этим жить и постараться повысить качество этой самой жизни.

Мир вокруг не идеален, сложную вещь сделать простой – невероятно сложно, поэтому всё чрезмерно усложнено. Людям нравиться чувствовать себя профессионалами, поэтому они оперируют сложными терминами, когда в этом нет необходимости, так они ощущают свою значимость и заполняют пустоту, которая образовалась из-за страха потерянного времени.

Усложнить можно всё: ты/Вы/вы

Почему я должен постоянно вспоминать о том, общался я с этим человеком или нет? Мы уже перешли на ты? Я пишу какое-то коммерческое предложение и мне надо «Выкнуть» или это ответ на рядовое письмо и достаточно простого «вы». Почему мне постоянно надо расстраиваться, когда мне с порога совершенно незнакомый человек начинает «тыкать», как будто я его друг с курилки?

Я принципиально всем «выкаю» с маленькой буквы и я убеждён, что это имеет свою цену. Наверняка есть люди, которым не нравится, что их назвали с маленькой буквы и они не ответят на моё письмо.

Так зачем эти сложности?

Когда я упомянул про повышения качества жизни, я имел ввиду некоторые правила, которые помогают быстро принимать решения и не жалеть об этом. Например я не использую сложные вещи постоянно, не общаюсь с людьми, которым нравится всё усложнять. Если вещь, которой вы пользуетесь каждый день – сложна, то это значит только то, что ей не уделили достаточно внимания. Если человек вам не может объяснить даже трудную для понимания тему простым языком, значит он сам не до конца понимает о чём говорит.

Есть ещё кое-что, что имеет не менее важное значение – время. Время – бесценный ресурс, а выражение «Время — деньги» из уст умных людей вызывает у меня улыбку с разочарованием (выглядишь как идиот и чувствуешь себя так же). Я до сих пор не знаю ни одного миллиардера, которому деньги помогли прожить дольше других людей.

Введение

Речь пойдёт об инструменте, который позволит вам построить полноценный и простой в использовании REST API за минимальное количество времени. Называется он – Python Eve.

К сожалению в Интернете очень много инструкций на эту тему, но все они вводят в заблуждение. Начинающие разработчики, начитавшись подобных статей, думают, что REST API это GET/POST/PUT/DELETE. Заказчики думают, что это дело пары часов. А когда они встречаются вместе, происходят магия в виде Express.js/Mongoose/Passport и ещё кучи хлама, который течёт и временами блокирует event-loop. Всё это запускается с помощью какого-нибудь supervisor, потому что иногда падает и надо как-то перезапускать.

И всё бы ничего, но вчера у меня состоялся разговор с хабра-пользователем, который предложил воспользоваться "Express.js, MongoDB, Mongoose, Passport, отладчиком WebStorm’a и головой на плечах". Похожие разговоры случались часто, поэтому я решил написать эту статью и «отсылать» ссылкой на неё.

Полноценный REST API?

Речь не только о реализации архитектурного стиля REST API, но и о протоколе HTTP, о валидации и кэшировании, о HATEOAS (wikipedia), о котором, похоже, вообще предпочитают не вспоминать. Затем нам понадобится фильтрация результатов и сортировка, постраничная навигация и частичное обновление записей. Потом мы задумаемся о целостности данных и условных запросах. Наверняка нам понадобится аутентификация, возможно захотим отображать данные не только в JSON, но и в XML. Это ещё про версионность и вложенные записи я не упомянул. Затем, как это обычно бывает, какой-то #$%$%^ начнёт долбить в наш могучий API с тяжёлым запросом и нам понадобится ограничить частоту запросов.

Даже, если представить, что разработкой такого API займётся невероятно крутой разработчик с 3-мя мониторами, отладчиком WebStorm’a и головой на плечах, он затратит на это не просто много времени, а очень много. Поддержка кодовой базы будет обходиться дорого, а внедрение новых функций будет долгим.

Но мы с вами простоту – любим, а время – уважаем. Так приступим же!

Установка

Это обычный python-пакет, поэтому устанавливается он стандартным способом:

$ pip install eve 

Если вас интересуют альтернативные методы установки, можете заглянуть в официальную документацию.

Быстрый старт

Перед тем, как мы начнём «творить» магию, нам понадобится база данных MongoDB. Если у вас её нет, вы можете воспользоваться любым бесплатным сервисом, например MongoLab. Регистрация займёт не больше минуты. После регистрации создайте базу данных и пользователя для этой базы.

Теперь давайте напишем минимальную версию нашего REST API. Для начала создадим главный файл run.py со следующим содержимым:

from eve import Eve app = Eve()  if __name__ == '__main__':     app.run() 

Теперь нам надо создать файл настроек settings.py:

# замените user, password, ds049945.mongolab.com, example на ваши данные доступа к БД. MONGO_URI = "mongodb://user:password@ds049945.mongolab.com:49945/example"  # По умолчанию Eve запускает API в режиме "read-only" (т.е. поддерживаются только GET запросы), # мы включаем поддержку методов POST, PUT, PATCH, DELETE. RESOURCE_METHODS = ['GET', 'POST', 'DELETE'] ITEM_METHODS = ['GET', 'PATCH', 'PUT', 'DELETE']  DOMAIN = {     # Описываем ресурс `/users`     'users': {         # Здесь мы описываем модель данных. Для валидации используется модуль Cerberus от автора Eve.         # Вы можете ознакомиться с ним в официальной документации модуля http://docs.python-cerberus.org/en/stable/.         # Либо прочитать заметки в официальной документации EVE http://python-eve.org/validation.html#validation.         'schema': {             'username': {                 'type': 'string',                 'minlength': 5,                 'maxlength': 32,                 'required': True,                 # уникальное поле (индекс не создаётся, просто значение должно быть уникальным)                 'unique': True,             },             'firstname': {                 'type': 'string',                 'minlength': 1,                 'maxlength': 10,                 'required': True,             },             'lastname': {                 'type': 'string',                 'minlength': 1,                 'maxlength': 15,                 'required': True,             },             'role': {                                  'type': 'list', # тип: список                 'allowed': ["author", "contributor"], # разрешаем использовать значения: "author", "contributor"             },             'location': {                 'type': 'dict', # тип: словарь                 # описываем "схему" словаря                 'schema': {                     'address': {'type': 'string'},                     'city': {'type': 'string'}                 },             },             'born': {                 'type': 'datetime',             },             'active': {                 'type': 'boolean',                 'default': True             }         }     },      # Описываем ресурс `/groups`     'groups': {         # Описываем модель данных (см. выше).         'schema': {             'title': {                 'type': 'string',                 'minlength': 5,                 'maxlength': 32,                 'required': True,                 'unique': True             },             'users': {                 'type': 'list',  # тип: список                 'default': [],   # по умолчанию: пустой список                 # описываем "схему" списка                 'schema': {                      'type': 'objectid', # тип данных: objectid                     # ссылаемся на запись в другой коллекции                     'data_relation': {                         'resource': 'users',  # на ресурс `users` (который мы описали выше)                         'field': '_id',  # на поле `_id`                         'embeddable': True                     }                 }             }         }     } } 

На мой взгляд здесь всё достаточно просто и вопросов возникнуть не должно. Если это не так – добро пожаловать в комментарии. Полный список параметров конфигурации вы можете глянуть в документации.

Всё готово, запускаем:

$ python3.5 run.py  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 

Прелюдия

Вы уже подумали, что сейчас мы начнём безбожно "curlить", но я вынужден вас разочаровать. Мы, со свойственным нам перфекционизмом, воспользуемся инструментом автора, который обладает чувством прекрасного:


Инструмент называется HTTPie и ставится в один клик одну команду:

$ pip install httpie 

Игрища и забавы

HTTPie вызывается командой "http". Для того, что бы отправить GET запрос к нашему API, выполним:

$ http http://0.0.0.0:5000/ HTTP/1.0 200 OK Content-Length: 99 Content-Type: application/json Date: Sun, 07 Feb 2016 18:13:33 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_links": {         "child": [             {                 "href": "users",                 "title": "users"             },             {                 "href": "groups",                 "title": "groups"             }         ]     } } 

Благодаря HATEOAS мы видим, что у нас есть 2 ресурса: users и groups. Заглянем внутрь:

» http http://0.0.0.0:5000/users HTTP/1.0 200 OK Content-Length: 166 Content-Type: application/json Date: Sun, 07 Feb 2016 18:20:41 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0 X-Total-Count: 0  {     "_items": [],     "_links": {         "parent": {             "href": "/",             "title": "home"         },         "self": {             "href": "users",             "title": "users"         }     },     "_meta": {         "max_results": 25,         "page": 1,         "total": 0     } } 

Давайте создадим пользователя johndoe:

$ http http://0.0.0.0:5000/users username=johndoe HTTP/1.0 422 UNPROCESSABLE ENTITY Content-Length: 184 Content-Type: application/json Date: Sun, 07 Feb 2016 18:22:44 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_error": {         "code": 422,         "message": "Insertion failure: 1 document(s) contain(s) error(s)"     },     "_issues": {         "firstname": "required field",         "lastname": "required field"     },     "_status": "ERR" } 

Первое, на что стоит обратить внимание, это на нашу команду:

$ http http://0.0.0.0:5000/users username=johndoe 

HTTPie увидел, что мы отправляем параметр username и превратил его в JSON:

{     "username": "johndoe" } 

Затем отправил нашему API методом POST. Давайте обратим внимание на ошибки:

"_issues": {     "firstname": "required field",     "lastname": "required field" } 

Мы видим сразу весь список ошибок валидации и это здорово. Исправим их и выполним запрос повторно:

$ http http://0.0.0.0:5000/users username=johndoe firstname=John lastname=Doe HTTP/1.0 201 CREATED Content-Length: 276 Content-Type: application/json Date: Sun, 07 Feb 2016 18:34:42 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_created": "Sun, 07 Feb 2016 18:34:41 GMT",     "_etag": "24509359443095dd05dece6d0eb7d98cce70b076",     "_id": "56b78e41cf7b35255aa5a1e6",     "_links": {         "self": {             "href": "users/56b78e41cf7b35255aa5a1e6",             "title": "User"         }     },     "_status": "OK",     "_updated": "Sun, 07 Feb 2016 18:34:41 GMT" } 

Ну вот, мы только что успешно создали нового пользователя. Давайте проверим так ли это:

$ http http://0.0.0.0:5000/users HTTP/1.0 200 OK Content-Length: 504 Content-Type: application/json Date: Sun, 07 Feb 2016 18:36:00 GMT Last-Modified: Sun, 07 Feb 2016 18:34:41 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0 X-Total-Count: 1  {     "_items": [         {             "_created": "Sun, 07 Feb 2016 18:34:41 GMT",             "_etag": "24509359443095dd05dece6d0eb7d98cce70b076",             "_id": "56b78e41cf7b35255aa5a1e6",             "_links": {                 "self": {                     "href": "users/56b78e41cf7b35255aa5a1e6",                     "title": "User"                 }             },             "_updated": "Sun, 07 Feb 2016 18:34:41 GMT",             "active": true,             "firstname": "John",             "lastname": "Doe",             "username": "johndoe"         }     ],     "_links": {         "parent": {             "href": "/",             "title": "home"         },         "self": {             "href": "users",             "title": "users"         }     },     "_meta": {         "max_results": 25,         "page": 1,         "total": 1     } } 

Нет никаких сомнений, что это так. Настало время попробовать перезаписать (обратите внимание, не отредактировать, а перезаписать) пользователя. Делается это с помощью метода PUT, который надо указать явно (если не указать, будет выполнен POST):

$ http put http://0.0.0.0:5000/users/56b78e41cf7b35255aa5a1e6 username=janedoe firstname="Jane" lastname="Doe" HTTP/1.0 403 FORBIDDEN Content-Length: 101 Content-Type: application/json Date: Sun, 07 Feb 2016 18:43:04 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_error": {         "code": 403,         "message": "An etag must be provided to edit a document"     },     "_status": "ERR" } 

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

Для того, что бы мы в такой ситуации не оказались, используется идентификатор ETag. В двух словах — это уникальный идентификатор, который генерируется Eve при каждом изменении записи. Используя этот идентификатор мы можем сказать нашему API, что хотим изменить запись только определённой версии и если она с тех была отредактирована, то наши изменения выполнены не будут. Делается это с помощью условного запроса с HTTP заголовком "If-Match":

$ http put http://0.0.0.0:5000/users/56b78e41cf7b35255aa5a1e6 "If-Match":"24509359443095dd05dece6d0eb7d98cce70b076" username=janedoe firstname="Jane" lastname="Doe" HTTP/1.0 200 OK Content-Length: 276 Content-Type: application/json Date: Sun, 07 Feb 2016 18:46:56 GMT ETag: 0138d193174528c205827ba9af25b7b8fb93940e Last-Modified: Sun, 07 Feb 2016 18:46:56 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_created": "Sun, 07 Feb 2016 18:34:41 GMT",     "_etag": "0138d193174528c205827ba9af25b7b8fb93940e",     "_id": "56b78e41cf7b35255aa5a1e6",     "_links": {         "self": {             "href": "users/56b78e41cf7b35255aa5a1e6",             "title": "User"         }     },     "_status": "OK",     "_updated": "Sun, 07 Feb 2016 18:46:56 GMT" } 

Обратите внимание каким образом мы передали HTTP заголовок HTTPie. Это не единственное, для чего может использоваться идентификатор ETag и условные запросы. Я здесь не буду останавливаться на этом, но советую вам ознакомиться с этой темой, если вы этого ещё не сделали.

Настало время создать новую группу. Для начала попробуем создать группу с несуществующим пользователем:

$ http http://0.0.0.0:5000/groups title="Friends" users:='["56b77466cf7b352414deb451"]' HTTP/1.0 422 UNPROCESSABLE ENTITY Content-Length: 220 Content-Type: application/json Date: Sun, 07 Feb 2016 19:14:31 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_error": {         "code": 422,         "message": "Insertion failure: 1 document(s) contain(s) error(s)"     },     "_issues": {         "users": {             "0": "value '56b77466cf7b352414deb451' must exist in resource 'users', field '_id'."         }     },     "_status": "ERR" } 

И действительно, пользователя с таким _id не существует. Обратите внимание как мы передаём список пользователей:

users:='["56b77466cf7b352414deb451"]' 

Подробнее вы можете почитать в официальной документации к HTTPie, которая такая же качественная, как и сам инструмент.

На этот раз мы укажем правильный _id:

» http http://0.0.0.0:5000/groups title="Friends" users:='["56b78e41cf7b35255aa5a1e6"]' HTTP/1.0 201 CREATED Content-Length: 278 Content-Type: application/json Date: Sun, 07 Feb 2016 19:21:42 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_created": "Sun, 07 Feb 2016 19:21:41 GMT",     "_etag": "c6fc02a0bd4bae92a1310be0748ff8bc971ff209",     "_id": "56b79945cf7b35255aa5a1e7",     "_links": {         "self": {             "href": "groups/56b79945cf7b35255aa5a1e7",             "title": "Group"         }     },     "_status": "OK",     "_updated": "Sun, 07 Feb 2016 19:21:41 GMT" } 

«Усё добра», как говорила моя прабабушка. Проверим:

$ http http://0.0.0.0:5000/groups HTTP/1.0 200 OK Content-Length: 488 Content-Type: application/json Date: Sun, 07 Feb 2016 19:24:50 GMT Last-Modified: Sun, 07 Feb 2016 19:21:41 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0 X-Total-Count: 1  {     "_items": [         {             "_created": "Sun, 07 Feb 2016 19:21:41 GMT",             "_etag": "c6fc02a0bd4bae92a1310be0748ff8bc971ff209",             "_id": "56b79945cf7b35255aa5a1e7",             "_links": {                 "self": {                     "href": "groups/56b79945cf7b35255aa5a1e7",                     "title": "Group"                 }             },             "_updated": "Sun, 07 Feb 2016 19:21:41 GMT",             "title": "Friends",             "users": [                 "56b78e41cf7b35255aa5a1e6"             ]         }     ],     "_links": {         "parent": {             "href": "/",             "title": "home"         },         "self": {             "href": "groups",             "title": "groups"         }     },     "_meta": {         "max_results": 25,         "page": 1,         "total": 1     } } 

Прабабушка оказалась бы права. На этом можно было бы закончить, но мы поступим иначе.

Получим группу с пользователями, которые в неё входят, в развёрнутом виде:

$ http http://0.0.0.0:5000/groups/56b79945cf7b35255aa5a1e7/\?embedded='{"users":1}' HTTP/1.0 200 OK Content-Length: 646 Content-Type: application/json Date: Sun, 07 Feb 2016 19:38:27 GMT ETag: c6fc02a0bd4bae92a1310be0748ff8bc971ff209 Last-Modified: Sun, 07 Feb 2016 19:21:41 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_created": "Sun, 07 Feb 2016 19:21:41 GMT",     "_etag": "c6fc02a0bd4bae92a1310be0748ff8bc971ff209",     "_id": "56b79945cf7b35255aa5a1e7",     "_links": {         "collection": {             "href": "groups",             "title": "groups"         },         "parent": {             "href": "/",             "title": "home"         },         "self": {             "href": "groups/56b79945cf7b35255aa5a1e7",             "title": "Group"         }     },     "_updated": "Sun, 07 Feb 2016 19:21:41 GMT",     "title": "Friends",     "users": [         {             "_created": "Sun, 07 Feb 2016 18:34:41 GMT",             "_etag": "0138d193174528c205827ba9af25b7b8fb93940e",             "_id": "56b78e41cf7b35255aa5a1e6",             "_updated": "Sun, 07 Feb 2016 18:46:56 GMT",             "active": true,             "firstname": "Jane",             "lastname": "Doe",             "username": "janedoe"         }     ] } 

Попросим тоже самое в XML:

$ http http://0.0.0.0:5000/groups/56b79945cf7b35255aa5a1e7/\?embedded='{"users":1}' "Accept":"application/xml" HTTP/1.0 200 OK Content-Length: 690 Content-Type: application/xml; charset=utf-8 Date: Sun, 07 Feb 2016 19:43:36 GMT ETag: c6fc02a0bd4bae92a1310be0748ff8bc971ff209 Last-Modified: Sun, 07 Feb 2016 19:21:41 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  <resource href="groups/56b79945cf7b35255aa5a1e7" title="Group">     <link href="groups" rel="collection" title="groups" />     <link href="/" rel="parent" title="home" />     <_created>Sun, 07 Feb 2016 19:21:41 GMT</_created>     <_etag>c6fc02a0bd4bae92a1310be0748ff8bc971ff209</_etag>     <_id>56b79945cf7b35255aa5a1e7</_id>     <_updated>Sun, 07 Feb 2016 19:21:41 GMT</_updated>     <title>Friends</title>     <users>         <_created>Sun, 07 Feb 2016 18:34:41 GMT</_created>         <_etag>0138d193174528c205827ba9af25b7b8fb93940e</_etag>         <_id>56b78e41cf7b35255aa5a1e6</_id>         <_updated>Sun, 07 Feb 2016 18:46:56 GMT</_updated>         <active>True</active>         <firstname>Jane</firstname>         <lastname>Doe</lastname>         <username>janedoe</username>     </users> </resource> 

Попробуем изменить только имя нашего пользователя (без полной перезаписи). Для этого воспользуемся HTTP методом PATCH:

$ http patch http://0.0.0.0:5000/users/56b78e41cf7b35255aa5a1e6 firstname=John "If-Match":"0138d193174528c205827ba9af25b7b8fb93940e" HTTP/1.0 200 OK Content-Length: 276 Content-Type: application/json Date: Sun, 07 Feb 2016 19:46:48 GMT ETag: 86f3495cf1d6edf301e25563099844bd816c5a3c Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_created": "Sun, 07 Feb 2016 18:34:41 GMT",     "_etag": "86f3495cf1d6edf301e25563099844bd816c5a3c",     "_id": "56b78e41cf7b35255aa5a1e6",     "_links": {         "self": {             "href": "users/56b78e41cf7b35255aa5a1e6",             "title": "User"         }     },     "_status": "OK",     "_updated": "Sun, 07 Feb 2016 19:46:47 GMT" } 

Что сказала бы бабуля?

» http http://0.0.0.0:5000/users/56b78e41cf7b35255aa5a1e6 HTTP/1.0 200 OK Content-Length: 431 Content-Type: application/json Date: Sun, 07 Feb 2016 19:50:06 GMT ETag: 86f3495cf1d6edf301e25563099844bd816c5a3c Last-Modified: Sun, 07 Feb 2016 19:46:47 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_created": "Sun, 07 Feb 2016 18:34:41 GMT",     "_etag": "86f3495cf1d6edf301e25563099844bd816c5a3c",     "_id": "56b78e41cf7b35255aa5a1e6",     "_links": {         "collection": {             "href": "users",             "title": "users"         },         "parent": {             "href": "/",             "title": "home"         },         "self": {             "href": "users/56b78e41cf7b35255aa5a1e6",             "title": "User"         }     },     "_updated": "Sun, 07 Feb 2016 19:46:47 GMT",     "active": true,     "firstname": "John",     "lastname": "Doe",     "username": "janedoe" } 

«Усё добра». Мне так понравилось, что я бы создавал пользователей пачками:

$ echo '[{"username": "userone", "firstname": "First", "lastname":"Last"},{"username":"usertwo", "firstname":"First", "lastname":"Last"}]' | http http://0.0.0.0:5000/users HTTP/1.0 201 CREATED Content-Length: 585 Content-Type: application/json Date: Sun, 07 Feb 2016 20:01:33 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0  {     "_items": [         {             "_created": "Sun, 07 Feb 2016 20:01:33 GMT",             "_etag": "6f397b570ef12769d372c902fa6149bb7e9eaf89",             "_id": "56b7a29dcf7b35255aa5a1e8",             "_links": {                 "self": {                     "href": "users/56b7a29dcf7b35255aa5a1e8",                     "title": "User"                 }             },             "_status": "OK",             "_updated": "Sun, 07 Feb 2016 20:01:33 GMT"         },         {             "_created": "Sun, 07 Feb 2016 20:01:33 GMT",             "_etag": "378f30b37724139c213a85079185226ab2b209f3",             "_id": "56b7a29dcf7b35255aa5a1e9",             "_links": {                 "self": {                     "href": "users/56b7a29dcf7b35255aa5a1e9",                     "title": "User"                 }             },             "_status": "OK",             "_updated": "Sun, 07 Feb 2016 20:01:33 GMT"         }     ],     "_status": "OK" } 

Найдём пользователя "John Doe" по его имени:

$ http http://0.0.0.0:5000/users\?where='{"firstname":"John"}' HTTP/1.0 200 OK Content-Length: 535 Content-Type: application/json Date: Sun, 07 Feb 2016 20:05:28 GMT Last-Modified: Sun, 07 Feb 2016 19:46:47 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0 X-Total-Count: 1  {     "_items": [         {             "_created": "Sun, 07 Feb 2016 18:34:41 GMT",             "_etag": "86f3495cf1d6edf301e25563099844bd816c5a3c",             "_id": "56b78e41cf7b35255aa5a1e6",             "_links": {                 "self": {                     "href": "users/56b78e41cf7b35255aa5a1e6",                     "title": "User"                 }             },             "_updated": "Sun, 07 Feb 2016 19:46:47 GMT",             "active": true,             "firstname": "John",             "lastname": "Doe",             "username": "janedoe"         }     ],     "_links": {         "parent": {             "href": "/",             "title": "home"         },         "self": {             "href": "users?where={\"firstname\":\"John\"}",             "title": "users"         }     },     "_meta": {         "max_results": 25,         "page": 1,         "total": 1     } } 

Отсортируем пользователей по их логину в обратном порядке:

$ http http://0.0.0.0:5000/users\?sort\=-username HTTP/1.0 200 OK Content-Length: 1203 Content-Type: application/json Date: Sun, 07 Feb 2016 20:08:08 GMT Last-Modified: Sun, 07 Feb 2016 20:01:33 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0 X-Total-Count: 3  {     "_items": [         {             "_created": "Sun, 07 Feb 2016 20:01:33 GMT",             "_etag": "378f30b37724139c213a85079185226ab2b209f3",             "_id": "56b7a29dcf7b35255aa5a1e9",             "_links": {                 "self": {                     "href": "users/56b7a29dcf7b35255aa5a1e9",                     "title": "User"                 }             },             "_updated": "Sun, 07 Feb 2016 20:01:33 GMT",             "active": true,             "firstname": "First",             "lastname": "Last",             "username": "usertwo"         },         {             "_created": "Sun, 07 Feb 2016 20:01:33 GMT",             "_etag": "6f397b570ef12769d372c902fa6149bb7e9eaf89",             "_id": "56b7a29dcf7b35255aa5a1e8",             "_links": {                 "self": {                     "href": "users/56b7a29dcf7b35255aa5a1e8",                     "title": "User"                 }             },             "_updated": "Sun, 07 Feb 2016 20:01:33 GMT",             "active": true,             "firstname": "First",             "lastname": "Last",             "username": "userone"         },         {             "_created": "Sun, 07 Feb 2016 18:34:41 GMT",             "_etag": "86f3495cf1d6edf301e25563099844bd816c5a3c",             "_id": "56b78e41cf7b35255aa5a1e6",             "_links": {                 "self": {                     "href": "users/56b78e41cf7b35255aa5a1e6",                     "title": "User"                 }             },             "_updated": "Sun, 07 Feb 2016 19:46:47 GMT",             "active": true,             "firstname": "John",             "lastname": "Doe",             "username": "janedoe"         }     ],     "_links": {         "parent": {             "href": "/",             "title": "home"         },         "self": {             "href": "users?sort=-username",             "title": "users"         }     },     "_meta": {         "max_results": 25,         "page": 1,         "total": 3     } } 

Ну и наконец-то удалим всех пользователей:

$ http delete http://0.0.0.0:5000/users HTTP/1.0 204 NO CONTENT Content-Length: 0 Content-Type: application/json Date: Sun, 07 Feb 2016 20:10:06 GMT Server: Eve/0.6.1 Werkzeug/0.10.4 Python/3.5.0 

Я бы не стал включать данную возможность в production. Указывается это в параметре RESOURCE_METHODS (стоит убрать из списка DELETE):

RESOURCE_METHODS = ['GET', 'POST', 'DELETE'] 

Заключение

Мы рассмотрели не все возможности Eve, но даже c учётом этого – получили законченный вариант REST API.

В данной статье я хотел обратить внимание на то, что настоящий RESTful сервис это гораздо больше, чем пара модулей для Node.js и в большинстве случаев нет необходимости разрабатывать такие вещи с нуля, а тем более писать статьи о том, как это сделать за один час. Достаточно взглянуть на историю развития проекта, что бы лишний раз убедиться в том, что пары часов/дней/недель недостаточно даже для хорошей команды.

Nicola Iarocci, автор Eve, создал прекрасный инструмент для быстрого развёртывания REST API, уделил этому достаточно внимания и сохранил простоту использования.

Ссылки

Подписывайтесь на меня в Twitter, я рассказываю о работе в стартапе, своих ошибках и правильных решениях, о python и всём, что касается веб-разработки.

P.S. Я ищу разработчиков в стартап, подробности у меня в профиле.

ссылка на оригинал статьи https://habrahabr.ru/post/276731/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *