Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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 метод

Slide 16

Slide 16 text

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 метод

Slide 17

Slide 17 text

Dmitry Petrov JMSSerializerBundle & PATCH метод

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

Dmitry Petrov Сливание DTO

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Dmitry Petrov SimpleThingsFormSerializerBundle 15 июля 2012 SimpleThingsFormSerializerBundle

Slide 24

Slide 24 text

Dmitry Petrov SimpleThingsFormSerializerBundle

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

Dmitry Petrov Отпуск

Slide 28

Slide 28 text

Dmitry Petrov Отпуск

Slide 29

Slide 29 text

Dmitry Petrov Отпуск

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Dmitry Petrov SimpleSerializer & POST / PATCH

Slide 35

Slide 35 text

Dmitry Petrov SimpleSerializer & POST метод

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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, валидация

Slide 44

Slide 44 text

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, валидация

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Dmitry Petrov REST APIs with Symfony2: The Right Way

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

Dmitry Petrov RESTful API, The End

Slide 58

Slide 58 text

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