REST w praktyce
...tej dobrej i tej złej
Jakub Kubrynski
jk@devskiller.com / @jkubrynski 1 / 42
jk@devskiller.com / @jkubrynski 2 / 42
"The Code is more what you'd call guidelines than actual rules. Welcome
aboard the Black Pearl, Miss Turner"
-- Cpt. Hector Barbossa to Elizabeth Swann
RT Ben Hale
jk@devskiller.com / @jkubrynski 3 / 42
Formal REST constraints
Client-Server
Stateless
Cache
Interface / Uniform Contract
Layered System
jk@devskiller.com / @jkubrynski 4 / 42
Richardson maturity model
http://martinfowler.com/articles/richardsonMaturityModel.html
jk@devskiller.com / @jkubrynski 5 / 42
POST vs PUT
jk@devskiller.com / @jkubrynski 6 / 42
POST vs PUT
POST creates new resources
jk@devskiller.com / @jkubrynski 7 / 42
POST vs PUT
POST creates new resources
PUT updates existing resources
PUT can create resource if ID is already known
jk@devskiller.com / @jkubrynski 8 / 42
REST without PUTs
for everyone who hates CRUD
jk@devskiller.com / @jkubrynski 9 / 42
REST without PUTs
for everyone who hates CRUD
all changes driven by events
POST to /domainEvents
jk@devskiller.com / @jkubrynski 10 / 42
Maybe PATCH?
partial update concept
no "out of the box" support
jk@devskiller.com / @jkubrynski 11 / 42
Caching
be aware - especially IE caches aggressively
better disable caching
jk@devskiller.com / @jkubrynski 12 / 42
Cache headers
cache-control: public, max-age=0, no-cache
public / private
no-cache
no-store
max-age
s-maxage
jk@devskiller.com / @jkubrynski 13 / 42
ETag
If-None-Match header set to entity uuid
if matches then "304 Not Modified"
uuid can be smart - entity id and version
"User:34652:15"
jk@devskiller.com / @jkubrynski 14 / 42
Compression
reduces response size dramatically
10 times smaller response is nothing special
usually really easy to enable
jk@devskiller.com / @jkubrynski 15 / 42
HATEOAS
jk@devskiller.com / @jkubrynski 16 / 42
HATEOAS
self-descriptive
jk@devskiller.com / @jkubrynski 17 / 42
HATEOAS
self-descriptive
client understands hypermedia
{
"name":"Alice",
"email":"alice_at_inchains.org"
"links":[
{"rel":"self","href":"/customers/1213"},
{"rel":"currentOrder","href":"/orders/14312"},
{"rel":"loyaltyAccount","href":"/accounts/11234"}
]
}
HTTP/1.1201Created
Location:http://api.myshop.com/orders/1234
jk@devskiller.com / @jkubrynski 18 / 42
@DanaDanger HTTP codes classification
20x: cool
30x: ask that dude over there
40x: you fucked up
50x: we fucked up
jk@devskiller.com / @jkubrynski 19 / 42
Exceptions
hide sensitive information
jk@devskiller.com / @jkubrynski 20 / 42
Exceptions
hide sensitive information
but include detailed information
{
"status":400,
"code":40483,
"message":"Incorrectbodysignature",
"moreInfo":"http://www.mycompany.com/errors/40483"
}
jk@devskiller.com / @jkubrynski 21 / 42
API Versioning
don't even think about
api.domain.com/v2/orders
URIs to the same resources should be fixed
between versions
jk@devskiller.com / @jkubrynski 22 / 42
API Versioning
don't even think about
api.domain.com/v2/orders
URIs to the same resources should be fixed
between versions
use Content-Type
1 version: application/vnd.domain+json
2 version: application/vnd.domain.v2+json
jk@devskiller.com / @jkubrynski 23 / 42
Filtering and sorting
GET /reviews?rating=5
GET /reviews?rating=5&sortAsc=author
jk@devskiller.com / @jkubrynski 24 / 42
Filtering and sorting
GET /reviews?rating=5
GET /reviews?rating=5&sortAsc=author
Dynamic queries are easier in POST body
jk@devskiller.com / @jkubrynski 25 / 42
Filtering and sorting
GET /reviews?rating=5
GET /reviews?rating=5&sortAsc=author
Dynamic queries are easier in POST body
POST /reviews/searches
GET /reviews/searches/23?page=2
jk@devskiller.com / @jkubrynski 26 / 42
Documentation
runnable with examples
Swagger
jk@devskiller.com / @jkubrynski 27 / 42
jk@devskiller.com / @jkubrynski 28 / 42
Stateless or not?
password hashing cost
session replication
load-balancing
jk@devskiller.com / @jkubrynski 29 / 42
Stateless or not?
password hashing cost
session replication
load-balancing
...
stateless session?
jk@devskiller.com / @jkubrynski 30 / 42
Security
SQL Injection
XSS
CSRF
XXE
jk@devskiller.com / @jkubrynski 31 / 42
CSRF - Cross-site request forgery
<imgsrc="https://api.mybank.com/transfers/from/1233/to/1234/amount/5000">
<formaction="https://api.mybank.com/transfers"method="POST">
<inputtype="hidden"name="from"value="1233"/>
<inputtype="hidden"name="to"value="1234"/>
<inputtype="hidden"name=amount"value="5000"/>
<inputtype="submit"value="CelebrityNudePhotos!"/>
</form>
jk@devskiller.com / @jkubrynski 32 / 42
CSRF - Cross-site request forgery
<imgsrc="https://api.mybank.com/transfers/from/1233/to/1234/amount/5000">
<formaction="https://api.mybank.com/transfers"method="POST">
<inputtype="hidden"name="from"value="1233"/>
<inputtype="hidden"name="to"value="1234"/>
<inputtype="hidden"name=amount"value="5000"/>
<inputtype="submit"value="CelebrityNudePhotos!"/>
</form>
One time request tokens
Correct CORS headers
jk@devskiller.com / @jkubrynski 33 / 42
CORS - Cross Origin Requests Sharing
Preflight request
OPTIONS/corsHTTP/1.1
Origin:http://www.domain.com
Access-Control-Request-Method:PUT
Access-Control-Request-Headers:X-Custom-Header
Host:api.mydomain.org
Accept-Language:en-US
Connection:keep-alive
User-Agent:Mozilla/5.0...
Preflight response
Access-Control-Allow-Origin:http://www.domain.com
Access-Control-Allow-Methods:GET,POST,PUT
Access-Control-Allow-Headers:X-Custom-Header
Content-Type:text/html;charset=utf-8
jk@devskiller.com / @jkubrynski 34 / 42
XML External Entity
<?xmlversion="1.0"encoding="utf-8"?>
<comment>
<text>Yeah!Ilikeit!</text>
</comment>
jk@devskiller.com / @jkubrynski 35 / 42
XML External Entity
<?xmlversion="1.0"encoding="utf-8"?>
<comment>
<text>Yeah!Ilikeit!</text>
</comment>
<?xmlversion="1.0"encoding="utf-8"?>
<!DOCTYPEmyentity[<!ENTITYa"Yeah!Ilikeit!">]>
<comment>
<text>&a;</text>
</comment>
jk@devskiller.com / @jkubrynski 36 / 42
# XML External Entity
<?xmlversion="1.0"encoding="utf-8"?>
<!DOCTYPEmyentity[<!ENTITYaSYSTEM"/etc/passwd">]>
<comment>
<text>&a;</text>
</comment>
jk@devskiller.com / @jkubrynski 37 / 42
# XML External Entity
<?xmlversion="1.0"encoding="utf-8"?>
<!DOCTYPEmyentity[<!ENTITYaSYSTEM"/etc/passwd">]>
<comment>
<text>&a;</text>
</comment>
<?xmlversion="1.0"encoding="utf-8"?>
<comment>
<text>root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
.....
</text>
</comment>
jk@devskiller.com / @jkubrynski 38 / 42
XML External Entity
<?xmlversion="1.0"encoding="utf-8"?>
<!DOCTYPEmyentity[
<!ENTITYa"abcdefghij1234567890">
<!ENTITYb"&a;&a;&a;&a;&a;&a;&a;&a;&a;&a">
<!ENTITYc"&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITYd"&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
...
<!ENTITYh"&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
]>
<comment>
<text>&h;</text>
</comment>
jk@devskiller.com / @jkubrynski 39 / 42
 
http://knowyourmeme.com/photos/531557 thx to @mihn
jk@devskiller.com / @jkubrynski 40 / 42
DDD training?
jk@devskiller.com / @jkubrynski 41 / 42
Thanks!
jk@devskiller.com / @jkubrynski 42 / 42

REST - the good and the bad parts