Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
168 lines (116 sloc) 7.13 KB

REST API

.. seealso::

   * https://ru.wikipedia.org/wiki/REST
   * `Для тех кто в Djang'е
     (http://blog.delaguardia.com.mx/pyramid-view-configuration-let-me-count-the-ways.html)
     <http://blog.delaguardia.com.mx/pyramid-view-configuration-let-me-count-the-ways.html>`_
   * http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/testing/testing_post_curl.html

REST API подразумевает под собой простые правила:

  • Каждый URL является ресурсом
  • При обращении к ресурсу методом GET возвращается описание этого ресурса
  • Метод POST добавляет новый ресурс
  • Метод PUT изменяет ресурс
  • Метод DELETE удаляет ресурс

Эти правила предоставляют простой CRUD интерфейс для других приложений, взаимодействие с которым происходит через протокол HTTP.

Соответствие CRUD операций и HTTP методов:

  • CREATE - POST
  • READ - GET
  • UPDATE - PUT
  • DELETE - DELETE

REST API интерфейс очень удобен для межпрограммного взаимодействия, например мобильное приложение может выступать в роли клиента, который манипулирует данными посредством REST.

Pattern matching

.. literalinclude:: /../examples/pyramid/restapi/pattern_matching_example.py
   :language: python3
   :emphasize-lines: 38-39, 15, 23, 31
   :linenos:

Пример выше добавляет View с тремя методами, каждый из которых вызывается при соответствующем GET, POST, DELETE запросе. Ресурсом здесь является конкретный человек, получить которого можно по URL http://localhost:8080/api/v1/people/123

Результатом запроса будет:

{"get": {}, "id": "123", "method": "GET"}

Для отправки POST запроса воспользуемся консольной утилитой 👨`curl`:

$ curl -X POST -d 'param1=value1&param2=value2' http://localhost:8080/api/v1/people/1

Результат запроса:

{"id": "1", "post": {"param1": "value1", "param2": "value2"}, "method": "POST"}

DELETE запрос выполняется по аналогии:

$ curl -X DELETE http://localhost:8080/api/v1/people/1

Результат запроса:

{"status": "success"}

Traversal

.. seealso::

    Метод URL диспетчеризации :ref:`traversal_routing`

В предыдущем примере показан только один ресурс - конкретный человек и в принципе все выглядит неплохо, пока не появится другой смежный ресурс, например список всех людей по адресу http://localhost:8080/api/v1/people

В этом случае, придется добавлять новый путь (rout), привязывать его к представлению (View) и самое неприятное менять само представление, или еще хуже писать новое. Таким образом с увеличением ресурсов, сложность REST API растет не пропорционально и в какой то момент код становится не читаемым из-за больших размеров и постоянно меняющейся логики во View.

Выход из данной ситуации - отделить ресурсы от представлений, тем самым вынести часть логики и сделать представления более универсальными.

Ресурсы

Ресурсы могут выглядеть так:

.. literalinclude:: /../examples/pyramid/restapi/traversal_example.py
   :language: python3
   :linenos:
   :pyobject: PeopleResource
   :caption: Список всех людей

PeopleResource представляет список всех людей и будет доступен по адресу http://localhost:8080/api/v1/people. PeopleResource имеет метод __getitem__, что делает его похожим на словарь. При обращении к объекту ресурса как к словарю, он вызовет эту функцию и передаст ключ в параметр people_id, например:

foo = PeopleResource()
bar = foo[123]  # Вернет объект PersonResource(123)

Метод __json__ определяет каким образом преобразовывать ресурс в json.

PersonResource представляет конкретного человека и будет доступен по адресу http://localhost:8080/api/v1/people/{id}. Здесь отличительной особенностью является то, что метод __json__ наследует часть словаря из класса PeopleResource, при помощи конструкции super:

.. literalinclude:: /../examples/pyramid/restapi/traversal_example.py
   :language: python3
   :linenos:
   :pyobject: PersonResource
   :emphasize-lines: 9
   :caption: Конкретный человек

View

Перепишем View таким образом, чтобы она возвращала только ресурс, а так-как ресурс уже содержит в себе информацию как отдавать json, то это представление будет универсальным как для PeopleResource, так и для PersonResource и возможно подойдет другим ресурсам которые мы будем писать в будущем.

.. literalinclude:: /../examples/pyramid/restapi/traversal_example.py
   :language: python3
   :linenos:
   :pyobject: RESTViewPeople
   :caption: Представление (View) для traversal ресурсов

Рендерер json по умолчанию ищет метод __json__ и если он есть то возвращает его результат вызова.

Route

Путь, в нашем случае, будет один, так-как вся структура вынесена в ресурсы (метод __getitem__).

config.add_route('rest_api', '/api/v1/*traverse', factory=rest_factory)

Полный пример

.. literalinclude:: /../examples/pyramid/restapi/traversal_example.py
   :language: python3
   :linenos: