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

Обработка сложных POST/PATCH запросов в RESTfu API

Dmitry Petrov
December 03, 2012
410

Обработка сложных POST/PATCH запросов в RESTfu API

Доклад с выступления на sfcampua 2012

Dmitry Petrov

December 03, 2012
Tweet

Transcript

  1. Dmitry Petrov
    [email protected]
    Обработка сложных
    POST/PATCH запросов в
    RESTful API

    View Slide

  2. ProFIT
    Dmitry Petrov
    Система управления
    производственными
    процессами типографии
    Product Fulfillment and Information Tracking

    View Slide

  3. ProFIT
    Dmitry Petrov
    Ежедневно:
    ~ 1 000 заказов
    ~1 000 000 печатной продукции
    1 час простоя ~ 25 000$
    Product Fulfillment and Information Tracking

    View Slide

  4. RESTful API
    Dmitry Petrov
    ~ 60 entity
    ~100 API endpoints
    Сложная бизнес логика
    RESTful API для ProFIT

    View Slide

  5. Dmitry Petrov
    GET /api/orders/12/items/fg45sf54
    Ответ сервера:
    {
    "id": "fg45sf54",
    "url": "http://localhost/api/orders/12/items/fg45sf54",
    "product": "business cards",
    "quantity": 1000,
    "previews": {
    "front": {
    "large": "http://localhost/large/front.jpg",
    "medium": "http://localhost/medium/front.jpg",
    "small": "http://localhost/small/front.jpg",
    },
    "back": {
    "large": "http://localhost/large/back.jpg",
    "medium": "http://localhost/medium/back.jpg",
    "small": "http://localhost/small/back.jpg",
    }
    }
    }
    GET /api/orders/12
    Ответ сервера:
    {
    "id": 12,
    "url": "http://localhost/api/orders/12",
    "client": {
    "firstname": "Dmitry",
    "lastname": "Petrov",
    "email": "",
    "phone": null,
    "address": {
    "country": "Russia",
    "city": "Saratov",
    "zip": 123456,
    "street": "Vavilova",
    "residentional": false
    }
    }
    }
    RESTful API, примеры GET

    View Slide

  6. Dmitry Petrov
    GET /api/product-box-types/12-type/associations
    Ответ сервера:
    [
    {
    "id": 1,
    "product": "business_cards",
    "quantity": 1000
    },
    ......
    ]
    GET /api/machines/KARAT+1/hot-folders
    Ответ сервера:
    [
    {
    "path":"/home/somepath/",
    "types": [
    "34-f-Type",
    "33-S-Type",
    ......
    ]
    },
    ......
    ]
    GET /api/press-sheets/134/label
    Ответ сервера:
    {
    "label": "epl string"
    }
    RESTful API, примеры GET

    View Slide

  7. Dmitry Petrov
    POST http://localhost/api/press-sheets/12/transition
    Тело запроса:
    {
    "transition": "start:printing:front",
    "note": null
    }
    POST http://localhost/api/orders,
    PUT http://localhost/api/orders/12
    Тело запроса:
    {
    "id": 12,
    "client": {
    "firstname": "Dmitry",
    "lastname": "Petrov",
    "email": "",
    "phone": null,
    "address": {
    "country": "Russia",
    "city": "Saratov",
    "zip": 123456,
    "street": "Vavilova",
    "residentional": false
    }
    }
    }
    RESTful API, примеры POST / PUT

    View Slide

  8. Dmitry Petrov
    PATCH http://localhost/api/orders/12
    Тело запроса:
    {
    "client": {
    "email": "",
    "phone": null
    }
    }
    PATCH http://localhost/api/orders/12
    Тело запроса:
    {
    "client": {
    "email": ""
    },
    "address": {
    "street": "Vavilova",
    "residentional": true
    }
    }
    Объект:
    {
    "id": 12,
    "client": {
    "firstname": "Dmitry",
    "lastname": "Petrov",
    "email": "[email protected]",
    "phone": "8-888-999",
    "address": {
    "country": "Russia",
    "city": "Saratov",
    "zip": 123456,
    "street": "Vavilova",
    "residentional": false
    }
    }
    }
    RESTful API, примеры PATCH

    View Slide

  9. Dmitry Petrov
    Призадумались . . .

    View Slide

  10. DTO
    Dmitry Petrov
    Data Transfer Object
    DTO
    attribute1: String
    attribute2: String
    Assembler
    createDTO
    updateDomainObject
    serialize
    deserialize
    DomainObject1
    attribute1: String
    DomainObject2
    attribute2: String

    View Slide

  11. Dmitry Petrov
    GET /api/orders/12
    Ответ сервера:
    {
    "id": 12,
    "url": "http://localhost/orders/12",
    "client": {
    "firstname": "Dmitry",
    "lastname": "Petrov",
    "email": "",
    "phone": null,
    "address": {
    "country": "Russia",
    "city": "Saratov",
    "zip": 123456,
    "street": "Vavilova",
    "residentional": false
    }
    }
    }
    Примеры DTO

    View Slide

  12. Dmitry Petrov
    {
    "transition": "start:printing:front",
    "note": null
    }
    {
    "label": "epl string"
    }
    [
    {
    "path":"/home/somepath/",
    "types": [
    "34-f-Type",
    ......
    ]
    },
    ......
    ]
    Примеры DTO

    View Slide

  13. Dmitry Petrov
    Уменьшение количества запросов
    Независимость от API
    "Заставляет думать"
    Преимущества паттерна DTO

    View Slide

  14. Dmitry Petrov
    FOSRestBundle
    JMSSerializerBundle
    LiipHelloBundle
    FOSCommentBundle
    Популярные бандлы и примеры

    View Slide

  15. Dmitry Petrov
    GET /api/orders/12
    Ответ сервера:
    {
    "id": 12,
    "url": "http://localhost/api/orders/12",
    "client": {
    "firstname": "Dmitry",
    "lastname": "Petrov",
    "email": "",
    "phone": null,
    "address": {
    "country": "Russia",
    "city": "Saratov",
    "zip": 123456,
    "street": "Vavilova",
    "residentional": false
    }
    }
    }
    JMSSerializerBundle & GET метод

    View Slide

  16. Dmitry Petrov
    POST /api/orders,
    Тело запроса:
    {
    "id": 12,
    "client": {
    "firstname": "Dmitry",
    "lastname": "Petrov",
    "email": "",
    "phone": null,
    "address": {
    "country": "Russia",
    "city": "Saratov",
    "zip": 123456,
    "street": "Vavilova",
    "residentional": false
    }
    }
    }
    JMSSerializerBundle & POST метод

    View Slide

  17. Dmitry Petrov
    JMSSerializerBundle & PATCH метод

    View Slide

  18. Dmitry Petrov
    $this->deserialize($request, 'Rest\OrderDTO', 'json');
    JMSSerializerBundle

    View Slide

  19. MERGE
    Dmitry Petrov
    $this->merge($oldDTO, $newDTO);
    JMSSerializerBundle

    View Slide

  20. Dmitry Petrov
    Сливание DTO

    View Slide

  21. Dmitry Petrov
    PATCH /api/orders/12
    Request:
    {
    "client": {
    "email": "",
    "phone": null
    }
    }
    JMSSerializerBundle & PATCH метод

    View Slide

  22. Проблемы / Минусы
    Dmitry Petrov
    GET - сериализация null значений
    PATCH - десериализация в объект
    PATCH - merge null значений
    MERGE - много бесполезного кода
    RESTful API, JMSSerializerBundle

    View Slide

  23. Dmitry Petrov
    SimpleThingsFormSerializerBundle
    15 июля 2012
    SimpleThingsFormSerializerBundle

    View Slide

  24. Dmitry Petrov
    SimpleThingsFormSerializerBundle

    View Slide

  25. Dmitry Petrov
    GET /api/orders/12
    Ответ сервера:
    {
    "id": "12",
    "url": "http://localhost/orders/12",
    "client": {
    "firstname": "Dmitry",
    "lastname": "Petrov",
    "email": "",
    "phone": "",
    "address": {
    "country": "Russia",
    "city": "Saratov",
    "zip": "123456",
    "street": "Vavilova",
    "residentional": "false"
    }
    }
    }
    SimpleThingsFormSerializerBundle

    View Slide

  26. Проблемы / Минусы
    Dmitry Petrov
    Конвертирование данных в string
    Отсутствие поддержки PATCH (v. 2.0)
    Идеологическая неприязнь
    Грязная смесь *Type и *DTO
    SimpleThingsFormSerializerBundle

    View Slide

  27. Dmitry Petrov
    Отпуск

    View Slide

  28. Dmitry Petrov
    Отпуск

    View Slide

  29. Dmitry Petrov
    Отпуск

    View Slide

  30. Требования
    Dmitry Petrov
    (Де)Сериализация объектов
    Сохранение типа у данных
    Кеширование метаданных
    Изобретаем велосипед

    View Slide

  31. Допущения
    Dmitry Petrov
    Выходной формат json
    Метадата хранится в yml
    Всегда есть get/set методы
    Изобретаем велосипед

    View Slide

  32. Через 36 часов...
    поезд Саратов - Киев идет 30 часов
    Dmitry Petrov
    SimpleSerializer
    SimpleSerializerBundle
    Подробности можно прочитать на хабре
    Изобретаем велосипед

    View Slide

  33. Преимущества
    Dmitry Petrov
    Библиотека
    Разделение правил сериализации от формата
    Отсутствие озвученных минусов
    "Интеллектуальная" десериализация
    SimpleSerializer

    View Slide

  34. Dmitry Petrov
    SimpleSerializer & POST / PATCH

    View Slide

  35. Dmitry Petrov
    SimpleSerializer & POST метод

    View Slide

  36. Что? Где? Когда?
    Dmitry Petrov
    Параметры запросов
    Объекты передачи данных
    Бизнес-логика
    RESTful API, валидация

    View Slide

  37. Параметры запросов
    Dmitry Petrov
    /api/orders/12
    /api/boxes/BOX-1-1
    /api/orders?valid=true
    RESTful API, валидация

    View Slide

  38. Routing requirements
    Dmitry Petrov
    RESTful API, валидация

    View Slide

  39. Dmitry Petrov
    ParameterChecker
    RESTful API, валидация

    View Slide

  40. Dmitry Petrov
    RESTful API, валидация

    View Slide

  41. Dmitry Petrov
    RESTful API, валидация

    View Slide

  42. Dmitry Petrov
    AbstractRestController
    RESTful API, валидация

    View Slide

  43. Dmitry Petrov
    PATCH /api/orders/12
    Тело запроса:
    {
    "client": {
    "email": "",
    "comment": "I'm hacker"
    }
    }
    Объект:
    {
    "id": 12,
    "client": {
    "firstname": "Dmitry",
    "lastname": "Petrov",
    "email": "[email protected]",
    "phone": "8-888-999",
    "address": {
    "country": "Russia",
    "city": "Saratov",
    "zip": 123456,
    "street": "Vavilova",
    "residentional": false
    }
    }
    }
    RESTful API, валидация

    View Slide

  44. Dmitry Petrov
    POST /api/press-sheets/12/transition
    Тело запроса:
    {
    "transition": "start:printing:front",
    "note": null,
    "comment": "I'm hacker"
    }
    POST /api/press-sheets/12/transition
    Тело запроса:
    {
    "transition": "start:printing:front",
    "comment": "I'm hacker"
    }
    Объект:
    {
    "transition": "start:printing:front",
    "note": null
    }
    RESTful API, валидация

    View Slide

  45. Dmitry Petrov
    Как, где и когда
    обрабатывать эти
    ситуации?
    RESTful API, валидация

    View Slide

  46. Dmitry Petrov
    REST APIs with Symfony2: The Right Way

    View Slide

  47. Dmitry Petrov
    Недостатки
    Рутиность
    Дублирование кода
    Работает лишь как фильтр
    REST APIs with Symfony2: The Right Way

    View Slide

  48. Dmitry Petrov
    "Интеллектуальная"
    десериализация
    3 режима десериализации:
    Strict, Medium strict, Non-strict
    +
    Поддержка групп
    SimpleSerializer

    View Slide

  49. Dmitry Petrov
    RESTful API, обработка DTO

    View Slide

  50. Dmitry Petrov
    RESTful API, обработка объекта

    View Slide

  51. Behat, PHPUnit
    Dmitry Petrov
    Контроллеры
    Data access layer
    Service layer
    RESTful API, тестирование

    View Slide

  52. Dmitry Petrov
    RESTful API, пример Behat сценария

    View Slide

  53. Проблемы
    Dmitry Petrov
    Время выполнения:
    Behat ~ 90 минут
    PHPUnit ~ 5 минут
    RESTful API, тестирование

    View Slide

  54. WSSE
    Dmitry Petrov
    Atom Authentication
    How to create a custom Authentication Provider
    EscapeWSSEAuthenticationBundle (v. 2.0)
    MopaWSSEAuthenticationBundle (v. 2.1)
    RESTful API, аутентификация

    View Slide

  55. WSSE Header
    Dmitry Petrov
    X-WSSE: UsernameToken Username="bob",
    PasswordDigest="quR/EWLAV4xLf9Zqyw4pDmfV9OY=",
    Nonce="d36e316282959a9ed4c89851497a717f",
    Created="2003-12-15T14:43:07Z"
    RESTful API, аутентификация

    View Slide

  56. Password digest
    Dmitry Petrov
    Base64 (SHA1 (Nonce + CreationTimestamp + Password))
    RESTful API, аутентификация

    View Slide

  57. Dmitry Petrov
    RESTful API, The End

    View Slide

  58. Вопросы?
    Dmitry Petrov
    @old_fightmaster
    RESTful API
    https://github.com/opensoft
    https://github.com/fightmaster
    Отдельное спасибо команде ProFIT

    View Slide