Привет Хабр! Меня зовут Владимир, мне 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/
Добавить комментарий