Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Handle complex POST/PATCH requests in RESTful API
Search
Dmitry Petrov
December 12, 2012
1
170
Handle complex POST/PATCH requests in RESTful API
Report with a speech on the 2012 sfcampua
Dmitry Petrov
December 12, 2012
Tweet
Share
More Decks by Dmitry Petrov
See All by Dmitry Petrov
Обработка сложных POST/PATCH запросов в RESTfu API
fightmaster
1
470
Featured
See All Featured
Why Our Code Smells
bkeepers
PRO
334
57k
Rebuilding a faster, lazier Slack
samanthasiow
79
8.7k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
Designing for Performance
lara
604
68k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
506
140k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
665
120k
Speed Design
sergeychernyshev
25
620
jQuery: Nuts, Bolts and Bling
dougneiner
61
7.5k
The World Runs on Bad Software
bkeepers
PRO
65
11k
The Art of Programming - Codeland 2020
erikaheidi
52
13k
Making Projects Easy
brettharned
115
5.9k
Why You Should Never Use an ORM
jnunemaker
PRO
54
9.1k
Transcript
Dmitry Petrov
[email protected]
Handle Complex POST/PATCH requests in RESTful API
ProFIT Dmitry Petrov The system of process control printing Product
Fulfillment and Information Tracking
ProFIT Dmitry Petrov Daily: ~ 1 000 orders ~1 000
000 print productions 1 hour of downtime ~ 25 000$ Product Fulfillment and Information Tracking
RESTful API Dmitry Petrov ~ 60 entity ~100 API endpoints
Complex business logic RESTful API for ProFIT
Dmitry Petrov GET /api/orders/12/items/fg45sf54 The server response: { "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 The server response: { "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, examples GET
Dmitry Petrov GET /api/product-box-types/12-type/associations The server response: [ { "id":
1, "product": "business_cards", "quantity": 1000 }, ...... ] GET /api/machines/KARAT+1/hot-folders The server response: [ { "path":"/home/somepath/", "types": [ "34-f-Type", "33-S-Type", ...... ] }, ...... ] GET /api/press-sheets/134/label The server response: { "label": "epl string" } RESTful API, examples GET
Dmitry Petrov POST http://localhost/api/press-sheets/12/transition Body of the request: { "transition":
"start:printing:front", "note": null } POST http://localhost/api/orders, PUT http://localhost/api/orders/12 Body of the request: { "id": 12, "client": { "firstname": "Dmitry", "lastname": "Petrov", "email": "", "phone": null, "address": { "country": "Russia", "city": "Saratov", "zip": 123456, "street": "Vavilova", "residentional": false } } } RESTful API, examples POST / PUT
Dmitry Petrov PATCH http://localhost/api/orders/12 Body of the request: { "client":
{ "email": "", "phone": null } } PATCH http://localhost/api/orders/12 Body of the request: { "client": { "email": "" }, "address": { "street": "Vavilova", "residentional": true } } Object: { "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, examples PATCH
Dmitry Petrov Become thoughtful . . .
DTO Dmitry Petrov Data Transfer Object DTO attribute1: String attribute2:
String Assembler createDTO updateDomainObject serialize deserialize DomainObject1 attribute1: String DomainObject2 attribute2: String
Dmitry Petrov GET /api/orders/12 The server response: { "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 } } } Examples of DTO
Dmitry Petrov { "transition": "start:printing:front", "note": null } { "label":
"epl string" } [ { "path":"/home/somepath/", "types": [ "34-f-Type", ...... ] }, ...... ] Examples of DTO
Dmitry Petrov Reducing the number of queries Independence from API
"Makes you think" The benefits of using DTO pattern
Dmitry Petrov FOSRestBundle JMSSerializerBundle LiipHelloBundle FOSCommentBundle Popular bundles and examples
Dmitry Petrov GET /api/orders/12 The server response: { "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 method
Dmitry Petrov POST /api/orders, Body of the request: { "id":
12, "client": { "firstname": "Dmitry", "lastname": "Petrov", "email": "", "phone": null, "address": { "country": "Russia", "city": "Saratov", "zip": 123456, "street": "Vavilova", "residentional": false } } } JMSSerializerBundle & POST method
Dmitry Petrov JMSSerializerBundle & PATCH method
Dmitry Petrov $this->deserialize($request, 'Rest\OrderDTO', 'json'); JMSSerializerBundle
MERGE Dmitry Petrov $this->merge($oldDTO, $newDTO); JMSSerializerBundle
Dmitry Petrov Merging DTO
Dmitry Petrov PATCH /api/orders/12 Request: { "client": { "email": "",
"phone": null } } JMSSerializerBundle & PATCH method
Problems / Disadvantages Dmitry Petrov GET - serialization of null
values PATCH - deserialized into an object PATCH - merge null values MERGE - a lot of useless code RESTful API, JMSSerializerBundle
Dmitry Petrov SimpleThingsFormSerializerBundle July 15, 2012 SimpleThingsFormSerializerBundle
Dmitry Petrov SimpleThingsFormSerializerBundle
Dmitry Petrov GET /api/orders/12 The server response: { "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
Problems / Disadvantages Dmitry Petrov Converting data into string Lack
of support PATCH method (v. 2.0) The ideological aversion The dirty mix *Type and *DTO SimpleThingsFormSerializerBundle
Dmitry Petrov Отпуск
Dmitry Petrov Отпуск
Dmitry Petrov Отпуск
Requirements Dmitry Petrov (Un)Serialization of objects Preservation of the type
in data Metadata cache Reinvent the wheel
Assumptions Dmitry Petrov The output format is json Metadata is
stored in yml There are get/set methods Reinvent the wheel
After 36 hours... train Saratov - Kiev is comming 30
hours Dmitry Petrov SimpleSerializer SimpleSerializerBundle Details can be found on the habr Reinvent the wheel
Benefits Dmitry Petrov This is library Separation of serialization rules
and format Absence voiced disadvantages "Inteligent" deserialization SimpleSerializer
Dmitry Petrov SimpleSerializer & POST / PATCH
Dmitry Petrov SimpleSerializer & POST method
What? Where? When? Dmitry Petrov Parameters of requests Data transfer
objects Business logic RESTful API, validation
Parameters of requests Dmitry Petrov /api/orders/12 /api/boxes/BOX-1-1 /api/orders?valid=true RESTful API,
validation
Routing requirements Dmitry Petrov RESTful API, validation
Dmitry Petrov ParameterChecker RESTful API, validation
Dmitry Petrov RESTful API, validation
Dmitry Petrov RESTful API, validation
Dmitry Petrov AbstractRestController RESTful API, validation
Dmitry Petrov PATCH /api/orders/12 Body of the request: { "client":
{ "email": "", "comment": "I'm hacker" } } Object: { "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, validation
Dmitry Petrov POST /api/press-sheets/12/transition Body of the request: { "transition":
"start:printing:front", "note": null, "comment": "I'm hacker" } POST /api/press-sheets/12/transition Body of the request: { "transition": "start:printing:front", "comment": "I'm hacker" } Object: { "transition": "start:printing:front", "note": null } RESTful API, validation
Dmitry Petrov How, where and when to handle these situations?
RESTful API, validation
Dmitry Petrov REST APIs with Symfony2: The Right Way
Dmitry Petrov Disadvantages Conventionalism Duplication of code Only works as
filter REST APIs with Symfony2: The Right Way
Dmitry Petrov "Inteligent" deserialization 3 modes of deserialization: Strict, Medium
strict, Non-strict + Support groups SimpleSerializer
Dmitry Petrov RESTful API, handle of DTO
Dmitry Petrov RESTful API, handle of domain object
Behat, PHPUnit Dmitry Petrov Controllers Data access layer Service layer
RESTful API, testing
Dmitry Petrov RESTful API, Behat scenario
Problems Dmitry Petrov Run time: Behat ~ 90 minutes PHPUnit
~ 5 minutes RESTful API, testing
WSSE Dmitry Petrov Atom Authentication How to create a custom
Authentication Provider EscapeWSSEAuthenticationBundle (v. 2.0) MopaWSSEAuthenticationBundle (v. 2.1) RESTful API, authentication
WSSE Header Dmitry Petrov X-WSSE: UsernameToken Username="bob", PasswordDigest="quR/EWLAV4xLf9Zqyw4pDmfV9OY=", Nonce="d36e316282959a9ed4c89851497a717f", Created="2003-12-15T14:43:07Z"
RESTful API, authentication
Password digest Dmitry Petrov Base64 (SHA1 (Nonce + CreationTimestamp +
Password)) RESTful API, authentication
Dmitry Petrov RESTful API, The End
Any questions? Dmitry Petrov @old_fightmaster RESTful API https://github.com/opensoft https://github.com/fightmaster Special
thanks to ProFIT team