Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Что не так с REST

Что не так с REST

Иван Гришаев

Deep Refactoring

August 31, 2017
Tweet

More Decks by Deep Refactoring

Other Decks in Education

Transcript

  1. REST Де-факто в индустрии Все фреймворки по REST Масса утилит

    (Swagger, RAML) Сервисы/сайты-API Священная корова
  2. REST Не факт, что было задумано именно так Так уж

    вышло Вложено много сил Все ли мы делаем правильно?
  3. Неконкретика Как передать версию? /api/v1/users /api/users?version=1 "Version": "1" {"name": "Ivan",

    "version": 1} (Как вообще версионировать? И нужно ли?)
  4. Неконкретика CRUD are not enough PUT vs POST vs PATCH

    Не отражает бизнес-логику
  5. Данные размазаны по всему запросу PUT /api/users/1 HTTP/1.1 Version: 1

    Authentication: Basic Base64String== {"name": "Ivan", "age": 31}
  6. Данные размазаны по всему запросу Нет смысла Слишком большой скоуп

    Такой Request трудно построить Только для веб-сервера верхнего уровня Чтение по частям
  7. Какая разница? import boto import time s3 = boto.connect_s3() bucket

    = s3.create_bucket('boto-demo-%s' % int(time.time())) key = bucket.new_key('mykey') key.set_contents_from_string("Hello World!") print key.get_contents_as_string() key.delete() bucket.delete()
  8. Какая разница? String cleanBucketName = bucketName.replaceAll("/$", ""); String uri =

    GCS_URI + cleanBucketName; HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(credential); GenericUrl url = new GenericUrl(uri); HttpRequest request = requestFactory.buildGetRequest(url); HttpResponse response = request.execute(); String content = response.parseAsString();
  9. Кеш? Не нужно Чаще всего кеширование как раз не нужно

    Костыли с random query arg Так и быть, разрешить GET + query string Varnish
  10. Зависимость от классов from django.conf.urls import url, include from django.contrib.auth.models

    import User from rest_framework import routers, serializers, viewsets class UserRest(BaseRestClass): def on_get(self, request): ... def on_put(self, request): ... def on_post(self, request): ... Как инициализировать класс? Как инициализировать Request? Как переиспользовать on_put?
  11. Request vs Data Запрос — это не данные, а состояние

    Огромный скоуп (сокет, сессия, ...) Нельзя сохранить Данные — постоянны
  12. SEA — single endpoint API Действие — что делать На

    сервере словарь "действие" ⇒ функция Одна функция-диспатчер
  13. SEA — single endpoint API { "action": "list_users", "active_only": true,

    # optional "order_by": "name", # optional "version": 1, "performer_id": 42, "signature": "671345234y6234" }
  14. SEA — single endpoint API Метаданные В словаре не только

    функции, но и схемы, права и тд
  15. SEA — single endpoint API actions = { "get_user": {

    "handler": api.get_user, "schema_in": schemas.get_user_in, "schema_out": schemas.get_user_out, "role": "admin", "doc": "Some API description", "added": "1.4.2", } }
  16. Одна точка входа А теперь еще клиент! var apiRoutes =

    { get_user: "/users/:id", list_users: "/users/", user_orders: "/users/:id/orders", ... }
  17. Одна точка входа /api {"action": "create_user", "name": "Ivan", "age": 31}

    /api {"action": "update_user", "user_id": 1, "name": "Juah"} /api {"action": "delete_order", "order_id": 42} ... 1 функция на сервере 1 функция на клиенте
  18. Масштабируется Не нужно писать новый класс Не нужно добавлять новый

    урл Просто новый ключ в словаре Версионирование по ключам (fallback)
  19. API знает все о себе Список всех апишек с доками

    Рендер документации как нужно нам Апишка, которая возврашает апишки, схемы, метаданные
  20. API знает все о себе список апишек {"action": "list_api"} документация

    к апишке {"action": "get_doc", "target": "list_api"} схемы данных {"action": "get_schemas", "target": "get_doc"}
  21. Сценарии {...}, {...}, {...} в файле, в коде VS GET

    /users/42 HTTP/1.1 POST /users/ HTTP/1.1 {...} PUT /users/42 HTTP/1.1 {...}
  22. Примеры actions = { "get_user": api_get_user, "update_user": api_update_user, "list_users": api_list_users

    } def api_update_user(data): user_id = data.pop("user_id") Users.objects.update(data).filter(id=user_id) return response_ok()
  23. Примеры (def action-rules {:displayfixtures/get-nearby {:handler #'nearby-displayfixtures :schema-in s/CQRSNearbyDisplayfixturesIn :schema-out s/CQRSNearbyDisplayfixturesOut}

    :displayfixtures/get-all-displayfixtures {:handler #'get-all-displayfixtures :schema-in s/CQRSParams :schema-out s/CQRSGetAllDisplayfixturesOut}
  24. Примеры (defmulti api :action) (defmethod api :users/get [{user_id :id}] ...)

    (defmethod api :users/update [params] ...) (defmethod api :default [params] {:status 404})
  25. Что это дает Независимость от транспорта Очереди, таски, Async Сценарии,

    обстрелы Масштабируемость Прозрачность Критичный взгляд