Полноценный 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. Я ищу разработчиков в компанию, подробности у меня в профиле.
    Поделиться публикацией

    Похожие публикации

    Комментарии 78

      –1
      Для гимна краткости пост слегка великоват.

      «Я пишу какое-то коммерческое предложение и мне надо «Выкнуть» или это ответ на рядовое письмо и достаточно простого «вы»» — перфекционист по определению должен в совершенстве знать правила родного языка и пользоваться ими (если Вы не русский человек — вычеркните это предложение).
        +7
        «Вы» с заглавной − это глупость. Перфекционизм требует избавляться от общепринятых глупостей.
          +8
          Просто слова — это глупость. Известно, что практически любую действительно важную мысль русский человек может выразить только при помощи междометий, предлогов-союзов и единственного слова из 3 букв. Префекционисты должны отбросить общепринятые глупости и изъясняться только так.
            +4
            Слово «Вы» с заглавной несет другой смысл, нежели, чем такое же слово «вы» без заглавной.
              +2
              По правилам русского языка местоимение «вы» можно использовать как в обращении к одному лицу, так и к нескольким. Само по себе употребление «вы» вместо «ты» свидетельствует об уважении. В чём тогда выражается этот «другой смысл»?
                –2
                Если слово «вы» используется как обращение к неопределённому кругу лиц (типа «уважаемый читатель»), то такое обращение принято писать со строчной буквы. Если слово «вы» используется для показания уважения при обращении к конкретному адресату (автор заранее знает человека, которому лично адресует текст), то его принято писать с заглавной. Также допускается авторский выбор. Т.е., автор может написать и личное обращение со строчной буквы, вкладывая в это намёк либо на не особо уважительное отношение к читателю, либо на маргинальную точку зрения относительно норм использования русского языка. Или при обращении к неопредлённому кругу лиц автор может использовать прописную букву, чтобы немного польстить любому из неопределённого круга лиц читателей (используется, как правило, в рекламе или пропаганде).
                  +5
                  Это взято из какого-то источника или ваше собственное понимание? В моих источниках другая информация:
                  1. «вы» при обращении к одному человеку допустимо по усмотрению автора и не является никаким намёком или маргинализацией,
                  2. «Вы» при обращении к нескольким лицам − банальная орфографическая ошибка.

                  Источники:
                    0
                    Да, верно, информация именно из вашей первой ссылки. Только, пожалуйста, не нужно побуквенного сканирования текстов, я уверен, что вы поняли границы и степень необязательности используемых правил и с моими ироничными замечаниями об уважении грамматических нацистов.

                    1) Употребление местоимения вы вместо ты при обращении к одному лицу само по себе уже представляет проявление уважительного отношения к этому лицу. Окончательное решение о написании Вы с прописной (для подчеркивания этого уважительного отношения) принимает автор текста.
                    2) Таким образом, местоимения Вы, Ваш пишутся с прописной буквы при обращении к одному лицу в текстах следующих жанров: [...] анкеты, рекламные листовки (текст, адресованный неконкретному лицу).
                      0
                      Я напомню: в комментарии, на который вы ответили, и выше по треду речь шла не о правиле орфографии, а о смысле, который сообщает заглавная буква.

                      Скорее всего, мы с вами по-разному поняли слово «смысл» в этом контексте.

                      Я имел в виду смысл, который, например, сообщает слову «всё» две маленькие точечки над буквой «е». Правилами разрешается их не ставить, но смысл слова от этого меняется. А между «вы» и «Вы» такой разницы в смысле не может быть.
                        0
                        > А между «вы» и «Вы» такой разницы в смысле не может быть.

                        Может, и разница в смысле объяснена в вашей первой ссылке и процитирована мною из неё же в ответ. У вас навязчивая идея, вы считаете некоторое правило «логичным», отрицая действительное (зафиксированное лингвистами) положение вещей в языке. Возможно, скоро таких «логиков» накопится критическое число, и правила немного упростят/подкорректируют, чтобы синхронизировать академические источники с реальным опытом носителей языка. Как произошло с кофием и шампунью в своё время, например.
                          0
                          Ну так вполне логично для «логиков» пропагандировать «логичные» варианты как логичные, чтобы быстрее накопилось критическое число употреблений и новая (или хорошо забытая старая) языковая норма сначала стало нормой де-факто, а потом была и зафиксирована «де-юре» в академических словарях.
                            0
                            Кто-то запрещает это делать? Я всего лишь указал на отличие желаемого от действительного. Т.е., «логикам» пока рано говорить, что они уже добились переписывания учебников, т.к. пока не добились. Вне зависимости от моего личного отношения к слову «вы».
                              0
                              Сначала люди начинают использовать новый вариант, а лишь потом переписывают учебники. В целом уже можно, наверное, утверждать, что литературно правильный, со всеми оттенками уважения или неуважения, выбор «вы»/«Вы» используют меньшее число людей, чем неправильный :)
                                0
                                Если эти люди приводят в качестве своей «лингвистической правоты» статью, из которой пропускают мимо своего внимания тезисы, противоречащие их взглядам, я обязательно укажу им на это. Такой уж я перфекционист.
                  +1
                  неверно.
                  «вы» надо писать с маленькой буквы за редкими исключениями
                +4
                Глупость сказали. Перфекционизм — стремление к совершенству. Все что дальше — сугубо относительно. Что вы выберете эталоном, то и будет.
                  0
                  Нет, совершенствование в заведомо низком искусстве подхалимажа не может быть вызвано перфекционизмом. А иного смысла, кроме подхалимства, «Вы» на письме не имеет.
                    +1
                    То ли дело «я» в английском, да?
                    В принципе, и предложения начинать с заглавной буквы, и пробел после знака препинания, а не перед, это излишества. Да и Васю незачем с заглавной писать — побудет васей, не облезет.
                      +1
                      >Нет, совершенствование в заведомо низком искусстве подхалимажа не может быть вызвано перфекционизмом

                      Можете привести какие-то аргументы в защиту этого утверждения, кроме собственных убеждений? Если нет, то — мимо.

                      > А иного смысла, кроме подхалимства, «Вы» на письме не имеет.

                      https://ru.wikipedia.org/wiki/%D0%92%D1%8B

                      >также употребляется при вежливом или официальном обращении к одному лицу

                      >Правила орфографии указывают случаи, когда местоимения «вы», «ваш» (во всех падежах и родах) в середине предложения пишутся с заглавной (прописной) буквы:

                      >как форма выражения вежливости при обращении к одному конкретному лицу в личной переписке, официальных документах и т. п., напр.: Поздравляем Вас…, Сообщаем Вам…; в ответ на Ваш запрос…;

                      >при официальном титуловании оба слова в сочетаниях Ваше (Его, Её) Величество, Ваше (Его, Её) Высочество[4].
                        –1
                        Можете привести какие-то аргументы в защиту этого утверждения


                        Во-первых, с точки зрения логики заглавная буква в «Вы» лишняя: обращение «вы» по сравнению с «ты» и так является уважительным. Масло масляное.

                        Во-вторых, ситуация с «Вы» («Ваш») и «вы» (ваш) довольно уникальная: никаких аналогий с правописанием других слов тут привести нельзя. А литературные языки стремятся к унификации правил, поэтому с «Вы» как наиболее надуманной формой обращения мы всё равно скоро попрощаемся.

                        В-третьих, не я один не люблю «Вы», то есть процесс очищения языка от излишней вычурности уже пошёл.
                          –1
                          мая твая панимать! скора вся наша будет така говорить. проста и панятна. лишняя сложнастя не нужна. процесса уже пошла.
                            –1
                            Не вы (я умышленно пишу по-вашему уровню, чтобы вы поняли) один не дружите с русским языком. И не вы один пренебрегаете вежливостью, в том числе и при переписке.
                            Но повода гордиться этим я не вижу.
                              –1
                              Артемий Лебедев § 165. Три правила про вы
                              www.artlebedev.ru/kovodstvo/sections/165
                                0
                                А мне кажется, что вы передёргиваете и паясничаете. Уж не знаю, умышленно или нет.

                                Вы сами за меня решили, что я то ли не люблю придерживаться правил языка, то ли являюсь сторонником перехода на фонетическую орфографию. Разумеется, это всё мимо.
                                –1
                                Без перехода на личности, но все же… ощущение подобных нюансов это результат сложившейся культуры общения у конкретного человека. Он повышается через общение с соответствующем окружением, чтение книг и т.п.
                                А что по теме, то для меня различие между «Вы» и «вы» — в степени дистанцированности от человека (знаком я с ним лично или нет; хочу ли я выделить формальность своего обращения), либо желании особо подчеркнуть его статус в моих глазах.
                                  –1
                                  >Во-первых, с точки зрения логики заглавная буква в «Вы» лишняя: обращение «вы» по сравнению с «ты» и так является уважительным. Масло масляное.

                                  «вы» с маленькой буквы используется на письме как обращение к множественному лицу. «Вы» с заглавной — к единственному. Банальное разделение для удобства при письме.

                                  Банальный пример (импровизация на тему письма в контору «Рога и Копыта», письмо адресовано конкретному адресату А):
                                  «Уведомляю вас, что с первого числа сего месяца, вы должны подавать документ Д не позднее двадцатого числа каждого месяца»

                                  Кого «вас»? Компанию или адресата?
                                  Кто «вы»? Лично «он(а)» со своей подписью и паспортом должен явиться в органы или компания через юридический (или какой-то другой) отдел должна подавать документы?

                                  Ситуация, по сути, такая же каки с е\ё — в большинстве случаев вполне очевидно, что имеется ввиду и люди про букву ё уже начинают забывать.

                                  Пример немного утрирован для наглядности.
                          –1
                          Перфекционист должен испытывать желание улучшить правила языка, которым пользуется. А еще он, как правильно заметил автор, должен испытывать желание оптимизировать свое общение. С этой точки зрения — автор абсолютно прав.
                            –1
                            Используя «Вы» в этом комментарии, вы как бы показываете, что вы не перфекционист?
                            0
                            Что насчёт CORS (https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)?
                            +4
                            Автор, держи наркоманское пять! Я также всем повторяю, что умение упрощать — один из главных навыков, приобретаемых с опытом и главный абстрактный маркер профессионализма. Тулза — прикольная. Хотелось бы поинтересоваться у знающих людей, есть ли что-то подобное на Go?
                              +2
                              Есть отличные плагины для браузеров (тот же postman в google chrome), намного практичнее, удобнее добавлять заголовки, есть история и.т.п.
                                +2
                                Консоль поработила моё сознание уже очень давно. Тут уже ни один плагин не сможет мне помочь.
                                +1
                                Есть bat, «inspired by httpie».
                                0
                                С jsonapi.org совместимо?
                                +5
                                Как фреймворк стыкуется с реляционными БД?
                                ps: про «вы с маленькой» писать не надо было. Не имеет отношения к теме, только лишний холивар.
                                –2
                                Я принципиально всем «выкаю»

                                Мы с женой разговариваем на «вы», так что я вас понимаю в этом плане )
                                  +1
                                  Всё написанное до Введения поглотилось на одном дыхании, ибо качественных статей с примерами на тему «упрощай» явно недостаточно.
                                  И тут, внезапно, программирование. ;)

                                  Может напишете общую статью?
                                    0
                                    Часто задумываюсь об этом, но постоянно ловлю себя на мысли – «кто я такой, что бы учить других жизни?».
                                      +2
                                      Воспримите это как акт обмена опытом. ;)
                                        +1
                                        Усложняете :) Есть желание — пишите, а не усложняйте себе жизнь размышлениями, не имеющими отношение к желанию :)
                                      +1
                                      Мне кажется Django REST Framework или Yii2 REST даст гораздо больше возможностей из коробки, так как там помимо чистого API сразу вам будет и админка и миграции базы и всё то, что умеет этот фреймворк + кучу плагинов и большее сообщество.

                                      Но это имхо. Если же действительно есть БД и нужно лишь апи к ней, то Eve конечно имеет право на существование.
                                        0
                                        Речь идёт о простоте. Кодовую базу Django я бы простой не назвал. Eve построен на Flask, куча плагинов у него тоже есть и с сообществом нет проблем.

                                        С ORM тоже непонятно. Если он мне не нужен? Лично я вообще уже забыл когда мне надо было использовать реляционную БД, а вот репликацию и шардинг я использую почти во всех своих проектах.

                                        Про Yii2 я ничего сказать не могу, PHP руками не трогаю.
                                          +1
                                          Ок, как обычно всё сводится к «всё зависит от задачи» =)
                                          0
                                          Из коробки в Django REST Framework'е из всего перечисленного разве что сериализация в json/xml есть.
                                          Впрочем eve тоже выглядит подозрительно просто.

                                          Интересно можно ли сделать CQRS на Eve, что бы так же просто было?!
                                          +1
                                          а где контроллеры? где писать бизнес логику? кастомная валидация? что-то сложнее «удалённой db по http» на этом написать можно?
                                          0
                                          Спасибо, очень хорошая статья. API — моя любимая тема для дискуссий. Очень мощная штука этот Eve, список возможностей радует глаз.
                                          И за тулзу отдельное спасибо, меня каждый раз curl в ступор вводит, когда надо руками запрос написать.
                                            0
                                            Не только вас. Я даже и не пытался запомнить параметры.

                                            Рад, что статья вам понравилась).
                                            0
                                            Надо в заголовок поста вынести язык/стек или сделать пост языконезависимым.
                                              +3
                                              Смотрел я на Python Eve некоторое время назад и знаете, у меня был опыт наступания на подобные грабли — Pinax назывались эти грабли. Всё это дело работает до поры до времени, пока у вас какой-нибудь хитрый случай не вылезет, где простым SELECT/UPDATE не отделаешься и начнётся «сладкая жизнь». Это ещё не говоря о том, что dict с конфигом разрастается в реальном проекте мама не горюй и подстветка синтаксиса такому коду уже не поможет.

                                              Кстати, интерфейсы взаимодействия в Eve меня пугают — JSON в GET параметрах выглядит странно, HATEOAS тоже не панацея (люди решили почему-то, что обязательно нужно иметь ссылки в API, но API используют машины и они не будут принимать никаких решений на основании содержимого ответа, они могут составить ссылки самостоятельно, в отличие от «кликающего пользователя»).

                                              P.S. Мои личные поиски «идеального» инструмента завершились на Flask-RESTplus, в котором, пожалуй да, приходится писать чуть больше кода, но зато есть возможность сделать то, что нужно и где нужно. Я в итоге собрал минимальный пример REST API сервера
                                                0
                                                люди решили почему-то, что обязательно нужно иметь ссылки в API, но API используют машины и они не будут принимать никаких решений на основании содержимого ответа, они могут составить ссылки самостоятельно, в отличие от «кликающего пользователя»


                                                Люди решили почему-то, что обязательно нужно иметь ссылки в HTML, но HTML используют машины и они не будут принимать никаких решений на основании содержимого ответа, они могут составлять ссылки самостоятельно, в отличие от «кликающего пользователя».

                                                А если серьёзно, то HATEOAS — это попытка, как минимум, частично стандартизировать автоматические правила составления ссылок. Самое очевидное применение — одновременно с представлением ресурса получить и список доступных текущему пользователю операций.
                                                  0
                                                  одновременно с представлением ресурса получить и список доступных текущему пользователю операций.
                                                  Ну и что, скажем, мобильное приложение будет с этой информацией по «доступным пользователю действиям» делать? Кнопки дорисовывать автоматически? Вот более развёрнутая мысль — Best Practices for Designing a Pragmatic RESTful API: Should you HATEOAS?
                                                    0
                                                    Не рисовать недоступные кнопки или рисовать их неактивными, чтобы не смущать пользователя кнопкой, по нажатию на которую он получает представление 403, 405 или иной ошибки.
                                                      0
                                                      Таким образом ответ нельзя закешировать, так как для разных пользователей будут отсутствовать некоторые ссылки, да и даже для одного пользователя нельзя закешировать — вдруг ему права урезали/добавили — инвалидировать кеш запаришься. Да и вообще, как по мне, то мухи должны быть отдельны от котлет. Хотите узнать какие действия можно производить над объектами — спросите об этом отдельно. Объект — это объект, а действия над ним — это отдельная история.
                                                        0
                                                        В терминах ООП объект и действия неотделимы :)
                                                          0
                                                          Даже в ООП вы не полезете в данные объекта за информацией о том что разршено делать с объектом, а спросите об этом у модуля безопасности, который, конечно, уже сам может решить проинспектировать запрашиваемый объект. Такой «модуль безопасности» в терминах RESTful API, как мне кажется, принято реализовывать по HTTP-запросу OPTIONS.
                                                  0
                                                  (люди решили почему-то, что обязательно нужно иметь ссылки в API, но API используют машины и они не будут принимать никаких решений на основании содержимого ответа, они могут составить ссылки самостоятельно, в отличие от «кликающего пользователя»).

                                                  Они всегда будут принимать решение на основании содержимого ответа. HATEOAS даёт возможность не плодить v1/v2/v3/v1000, для случаев, когда там просто изменился один URL. Можно «хардокить» везде ссылки и пересобирать мобильные приложение и фронтенд каждый раз, если так удобнее.

                                                  Если говорить о каком-нибудь инструменте автоматического генерирования админки, то без HATEOAS там вообще обойтись не получится.

                                                  Кстати, интерфейсы взаимодействия в Eve меня пугают — JSON в GET параметрах выглядит странно

                                                  Не буду отрицать.

                                                  Всё это дело работает до поры до времени, пока у вас какой-нибудь хитрый случай не вылезет, где простым SELECT/UPDATE не отделаешься и начнётся «сладкая жизнь».

                                                  Вы документацию читали? Есть "хуки" на каждое событие, если их их мало, то есть все возможности Flask. Вы же сами дальше пишите, что Flask используете.

                                                  Давно работаю с Eve и такого рода проблем точно не встречал.

                                                  Это ещё не говоря о том, что dict с конфигом разрастается в реальном проекте мама не горюй и подстветка синтаксиса такому коду уже не поможет.

                                                  Есть альтернативные, более короткие, варианты описания моделей? Большой конфиг, разделите его на модели, модели опишите в YAML и всё будет здорово.

                                                  Или вам удобнее модели в Django кодом описывать?
                                                    0
                                                    Они всегда будут принимать решение на основании содержимого ответа. HATEOAS даёт возможность не плодить v1/v2/v3/v1000, для случаев, когда там просто изменился один URL. Можно «хардокить» везде ссылки и пересобирать мобильные приложение и фронтенд каждый раз, если так удобнее.
                                                    Для случая просто смены URL я оставляю старый URL, в документации помечаю deprecated, а в следующей версии, когда других изменений уже накопилось много — удаляю.

                                                    Если говорить о каком-нибудь инструменте автоматического генерирования админки, то без HATEOAS там вообще обойтись не получится.
                                                    По The OpenAPI Specification (fka The Swagger Specification) строить админку куда легче, чем ходить по всем URL.

                                                    Вы документацию читали? Есть «хуки» на каждое событие, если их их мало, то есть все возможности Flask. Вы же сами дальше пишите, что Flask используете.
                                                    Хуки хуками, а бизнес логику куда девать? Её не существует? Самое первое препятствие — роли пользователей, в частности «владелец» объекта и «менеджер», который имеет доступ в том числе ко всем объектам своих подчинённых (в простом случае). Вот так я это реализовал в своём примере — github.com/frol/flask-restplus-server-example/blob/master/app/modules/teams/resources.py#L79

                                                    Или вам удобнее модели в Django кодом описывать?
                                                    Да, почему-то в коде мне их удобнее описывать, могу использовать наследования, например: github.com/frol/flask-restplus-server-example/blob/master/app/modules/teams/schemas.py
                                                      0
                                                      Для случая просто смены URL я оставляю старый URL, в документации помечаю deprecated, а в следующей версии, когда других изменений уже накопилось много — удаляю.

                                                      Ну об этом и речь. Я не буду ничего помечать «deprecated» и удалять в следующей версии. Я просто поменяю URL.

                                                      По The OpenAPI Specification (fka The Swagger Specification) строить админку куда легче, чем ходить по всем URL.

                                                      Я не такой любитель стандартов и спецификаций. В начале статьи я написал о простоте, мне чем проще, тем лучше.

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

                                                      Хуки хуками, а бизнес логику куда девать? Её не существует? Самое первое препятствие — роли пользователей, в частности «владелец» объекта и «менеджер», который имеет доступ в том числе ко всем объектам своих подчинённых (в простом случае).

                                                      Стандартная задача, решается легко. Вариантов реализации много, примеры можно найти в официальной документации.
                                                  +1
                                                  Да, ещё меня в Python Eve удивила реализация PATCH. Please. Don't Patch Like An Idiot. — William Durand. Если кратко, то есть RFC 6902, а в Eve решили придумать свой велосипед. (Кстати, в моём примере RESTful API сервера, по ссылке выше, PATCH реализован как рекомендует RFC)
                                                    –1
                                                    Это решили не в Python Eve. Я часто встречал такое использование и это уже, похоже, внегласное соглашение. Всё, как обычно для простоты использования. Посмотрите на примеры из статьи, которую вы привели в пример:

                                                    [
                                                        { "op": "test", "path": "/a/b/c", "value": "foo" },
                                                        { "op": "remove", "path": "/a/b/c" },
                                                        { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
                                                        { "op": "replace", "path": "/a/b/c", "value": 42 },
                                                        { "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
                                                        { "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
                                                    ]
                                                    

                                                    Так они хотят вносить изменения в JSON. Для XML предлагают вообще XML Path использовать. Но мне надо просто изменить значение, я не хочу ничего знать об XML Patch. Завтра вам понадобится использовать какой-нибудь другой формат данных, например YAML и надо будет придумать свой велосипед. Это если закрыть глаза на то, что составление такого запроса на порядок сложнее и не упростит реализацию мобильного клиента. А парсинг таких запросов на сервере, их валидация и обработка буду занимать больше времени.

                                                    У меня был опыт разработки WebDAV, в котором используется много стандартов. Также имел опыт создания CalDAV и CardDAV, которые являются дополнениями к WebDAV и могу вам сказать следующее: никто и никогда полностью не соблюдает стандарты, в особенности, когда они сложны. Реализация более-менее функционального сервиса WebDAV превращается в большую кучу патчей для всех распространённых клиентов.

                                                    Если вы попробуете использовать штатный WebDAV клиент Windows/OS X, то вряд ли найдёте хотя бы несколько серверов, с которыми у вас не будет проблем. Про CalDAV/CardDAV клиентов я промолчу. А ведь всё описано стандартами.

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

                                                    Стандарты это круто, пока следуют им другие.
                                                      +1
                                                      Сложно? Да, чуть сложнее, чем решение в лоб, зато расширяемо. Не нравится RFC — напишите свою, но реализация «мне так нравится» подходит для своего личного пользования, а для фреймворка — это минус. OpenAPI (Swagger) спецификацией можно описать любой каприз и после этого у вас будет и интерактивная документация, и автогенерируемые клиенты на куче языков, но Eve не пошёл путём интеграции с Swagger, так что да, написать сервер просто, но потом клиента к нему писать придётся самому и работать с особенностями Eve на клиенте — тоже.

                                                      Опять же, можете у меня в примере посмотреть и как это выглядит в Swagger документации (Docker образ поднимается за полминуты), и в коде (я пока не оформил этот случай в отдельную обёртку, но думаю об этом), и в:
                                                      OpenAPI (Swagger) Specification JSON, который автоматически собирается Flask-RESTplus
                                                      "patch": {
                                                      	"description": "**PERMISSIONS: Owner/Supervisor/Admin may execute this action.**",
                                                      	"operationId": "patch_team_by_id",
                                                      	"parameters": [
                                                      		{
                                                      			"in": "path",
                                                      			"name": "team_id",
                                                      			"required": true,
                                                      			"type": "integer"
                                                      		},
                                                      		{
                                                      			"in": "body",
                                                      			"name": "body",
                                                      			"required": true,
                                                      			"schema": {
                                                      				"items": {
                                                      					"properties": {
                                                      						"op": {
                                                      							"enum": [
                                                      								"add",
                                                      								"test",
                                                      								"remove",
                                                      								"replace"
                                                      							],
                                                      							"location": "json",
                                                      							"type": "string"
                                                      						},
                                                      						"path": {
                                                      							"enum": [
                                                      								"/title"
                                                      							],
                                                      							"location": "json",
                                                      							"type": "string"
                                                      						},
                                                      						"value": {
                                                      							"location": "json",
                                                      							"type": "string"
                                                      						}
                                                      					},
                                                      					"required": [
                                                      						"op",
                                                      						"path"
                                                      					]
                                                      				},
                                                      				"location": "json",
                                                      				"type": "array"
                                                      			}
                                                      		}
                                                      	],
                                                      	"responses": {
                                                      		"200": {
                                                      			"description": "Success",
                                                      			"schema": {
                                                      				"$ref": "#/definitions/DetailedTeamSchema"
                                                      			}
                                                      		},
                                                      		"401": {
                                                      			"description": "Authentication with teams:write scope(s) is required",
                                                      			"schema": {
                                                      				"$ref": "#/definitions/401HTTPErrorSchema"
                                                      			}
                                                      		},
                                                      		"403": {
                                                      			"description": "Owner/Supervisor/Admin may execute this action.",
                                                      			"schema": {
                                                      				"$ref": "#/definitions/403HTTPErrorSchema"
                                                      			}
                                                      		},
                                                      		"404": {
                                                      			"description": "Team not found.",
                                                      			"schema": {
                                                      				"$ref": "#/definitions/404HTTPErrorSchema"
                                                      			}
                                                      		},
                                                      		"409": {
                                                      			"description": "A conflict happened while processing the request.  The resource might have been modified while the request was being processed.",
                                                      			"schema": {
                                                      				"$ref": "#/definitions/409HTTPErrorSchema"
                                                      			}
                                                      		},
                                                      		"422": {
                                                      			"description": "The request was well-formed but was unable to be followed due to semantic errors.",
                                                      			"schema": {
                                                      				"$ref": "#/definitions/422HTTPErrorSchema"
                                                      			}
                                                      		}
                                                      	},
                                                      	"security": [
                                                      		{
                                                      			"oauth2": [
                                                      				"teams:write"
                                                      			]
                                                      		}
                                                      	],
                                                      	"summary": "Patch team details by ID",
                                                      	"tags": [
                                                      		"teams"
                                                      	]
                                                      }
                                                      

                                                        –1
                                                        Я не буду рассуждать на тему: правильно/неправильно. Я не разработчик фреймворка и не учавствовал в создании стандартов. Я выражаю лишь своё мнение — я считаю вариант Eve удобнее. Более того, вариант Eve используется в большинстве случаев.

                                                        но потом клиента к нему писать придётся самому и работать с особенностями Eve на клиенте — тоже

                                                        Тоже самое можно сказать и о вашей спецификации и придерживаться её – сложнее. Хотя бы потому, что она сама на порядок сложнее. По крайней мере для меня.

                                                  –1
                                                  Перфекционист на маке? О_о или в прелюдии не ваши скриншоты?)

                                                  А по поводу перфекционизма я задумался после прочтения недавних статей на хабре или гиктаймзе про эволюцию и глаз, на сколько он не совершенен, и то что мы так хорошо видим это доработка хаками… если мы по натуре не совершены стоит ли идти к совершенству, какая-то такая филосовская мысль, перфекционистом сложно жить )
                                                    –1
                                                    А на чём должен работать перфекционист, как не на маке?

                                                    Перфекционизм — это стремление к совершенству. Тот факт, что мы являемся самыми умными существами на планете и остерегаемся во время прогулки только себе подобных, позволяет говорить о том, что мы одна из лучших работ эволюции.

                                                      –1
                                                      Мак подогнан под какую то среднюю ЦА, не минималистичен, не гибок, множество спорных моментов, имхо конечно же.
                                                    0
                                                    Пытаюсь повторить примеры,
                                                    http htp://127.0.0.1:5000/
                                                    
                                                    отрабатывает нормально, а
                                                    http http://127.0.0.1:5000/users
                                                    выдаёт «500 Internal Server Error»
                                                    Не подскажете, где могла собака порыться?
                                                      0
                                                      Гляньте в логи Eve, там должно быть описание ошибки. Скорее всего проблемы с подключением к БД, например пароль неправильный и т.д.
                                                      +1
                                                      Если человек вам не может объяснить даже трудную для понимания тему простым языком, значит, он сам не до конца понимает о чём говорит.

                                                      Пожалуй эту фразу я сохраню в цитатнике.
                                                        +1
                                                        Этот тезис (как и многие другие «простые истины») практически бесполезен (и даже вреден) в реальной жизни. Это чрезмерное упрощение. http://lesswrong.ru/w/Ожидая_короткие_понятийные_расстояния
                                                          0
                                                          Расскажите об этом Стиву Возняку. Отец ему начал рассказывать принципы электротехники, когда он еще не достиг четырех лет.
                                                            0
                                                            Я не думаю, что по прошествии года, в пять лет, Стив стал профессионально разбираться в "принципах электротехники". Всё же есть разница между идеей "передать информацию из одной головы в другую" и "заинтересовать новичка, не используя сложных терминов и приводя интересные примеры да аналогии". Ну и, справедливости ради, я не знаю, что именно говорил Возняку его отец.
                                                        0
                                                        Спасибо. Полезный инструмент, намного удобнее Django REST Framework. Жаль, нет интеграции с Django.

                                                        P.S.
                                                        Упс, ошибка. Я выше упоминал о целостности данных.
                                                        ссылка битая.
                                                          0
                                                          Спасибо, поправил.

                                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                        Самое читаемое