What would go wrong if we use verbs in #REST #URL.
Whether there is some rationale behind it or it just REST dogma. Are there any “#RESTguidelines”?
In this session we will explore how to model our services so that we follow the RESTful way adhering to HTTP specification.
4. Its Just a STYLE!
The REST architectural style was developed by W3C
Technical Architecture Group (TAG) in parallel with
HTTP/1.1, based on the existing design of HTTP/1.0.
5. Roy Fielding’s paper on REST architectural style
Architectural Styles and the Design of Network-based Software
Architectures
DISSERTATION
submitted in partial satisfaction of the requirements for the degree of
DOCTOR OF PHILOSOPHY
in Information and Computer Science
by
Roy Thomas Fielding
2000
7. HTTP Specification
GET ../foods/1 would get you the food with id 1.
PUT ../foods/2 would update the food with id 2.
POST ../foods will add a new food.
DELETE ../foods/1 will delete the food resource with
id 1.
8. But what about other verbs???
--- approve
-- reject
-- cancel
-- search
-- increase
-- decrease
…..
…..
9. Can I just put them in my URL??
what would go wrong?
Lets see a video
http://www.youtube.com/watch?v=65ilZ8esAUs
13. 1. Safe and Unsafe Methods
excerpt from w3 HTTP Specification…
…..GET and HEAD methods SHOULD NOT have the significance of
taking an action other than retrieval.
These methods ought to be considered "safe". This allows user agents to
represent other methods, such as POST, PUT and DELETE, in a special
way, so that the user is made aware of the fact that a possibly unsafe
action is being requested....
GET and HEAD should have no side-effects : They
should not change the state.
Clearly we can not use GET in our API..
14. 2.Idempotency
An idempotent HTTP method can be called many times
without different outcomes.
a = 4 ->idempotent
a++ -> non idempotent
...Methods can also have the property of "idempotence" in that (aside from
error or expiration issues) the side-effects of N > 0 identical requests is
the same as for a single request. The methods GET, HEAD, PUT and
DELETE share this property…
..POST is non-idempotent..
15. Fault Tolerant - Idempotent Methods
- if request timed out - can you safely retry?
- no need to worry if idempotent
17. Caching
Non-safe and non-idempotent methods will never be
cached by any middleware proxies.
Safe Methods like GET and HEAD are candidate for
caching.
18. caching with GET
Every GET call is a candidate for caching..
If you have method :
HTTP/1.1 GET …./users/1/update
This might not actually update and return you the
cached result.
19. Bad Urls hamper caching!
HTTP 1.1 GET
http://myTestApp/page4.do?
dau22.oid=5199&UserCtxParam=0&GroupCtxParam=
0&dctx1=25&ctx1=US&crc=712082047
24. Benefits of HTTP Caching
- Server side caching is expensive..
- Reduce latency
- Reduce network traffic
-CDNs can leverage proxy caches.
25. Leverage Caching effectively
With great power comes great responsibility...
How to control caching effectively?
Invalidations?
Cache expiry?
Stale cache?
Volatile data?
27. Expires Header
● HTTP 1.0
So, if we made an API call to retrieve data
………..
GET
/users/1
The response header would be:
HTTP/1.1
200
OK
Content-‐Type:
application/xml
Expires:
Tue,
25
Aug
2013
16:00
GMT
-‐-‐-‐-‐-‐
<user
id="1">...</users>
28. JAX-RS support for expires..
@Path("{id}")
@GET
@Produces(MediaType.APPLICATION_XML)
public Response getUserXML(@PathParam("id") Long id){
User user = userDB.get(id);
ResponseBuilder builder = Response.ok
(user,MediaType.APPLICATION_XML);
//Putting expires header for HTTP browser caching.
Calendar cal = Calendar.getInstance();
cal.set(2013,7,25,16,0);
builder.expires(cal.getTime());
return builder.build();
}
29. HTTP 1.1
support CDNs, proxy caches and revalidations there
was a need for more enhanced headers with richer set of
features, having more explicit controls.
30. Cache-Control
Cache-Control has a variable set of comma-delimited
directives that define who,how and for how long it can
be cached. Lets explore few of them:
-private/public : these are accessibility directives,
private means a browser can cache the object but the
proxies or CDNs can not and public makes it cachable
by all.
-no-cache,no-store,max-age are few others where
name tells the story.
31. JAX-RS support for Cache-Control
@Path("{id}")
@GET
@Produces(MediaType.APPLICATION_XML)
public Response getUserXMLwithCacheControl(@PathParam("id")
Long id){
User user = userDB.get(id);
CacheControl cc = new CacheControl();
cc.setMaxAge(300);
cc.setNoStore(true);
cc.setPrivate(true);
ResponseBuilder builder = Response.ok
(user,MediaType.APPLICATION_XML);
builder.cacheControl(cc);
return builder.build();
}
32. Validation Headers and Conditional GETs
When cache is stale, client can ask server if cache still valid
To be able to revalidate client needs additional headers
beyond Cache-Control from a server response
•Last-Modified - a date when the resource was last modified
•ETag - a unique hash-like key that identifies a version of the
resource
Client should cache these headers along with response body
To revalidate client sends conditional GETs using values of these
header tags.
33. Last-Modified and If-Modified-Since
Server
sends
in
response
header
HTTP/1.1
200
OK
....
Cache-‐Control:
max-‐age=1000
Last-‐ModiQied:
Mon,
19
aug
2013
16:00
IST
Client
revalidates
using
conditional
GET
GET
/users/23
HTTP/1.1
If-‐ModiQied-‐Since:
Mon,
19
aug
2013
16:00
IST
in
case
it
is
modiQied
after
this
date;
a
response
code
200
(OK)
with
current
value
of
resource
will
be
sent.
And
if
the
data
is
not
modiQied
a
response
code
of
“304″
34. Etag and If-None-Match
●
●
●
an MD5 hash value.
generated from resource is sent by server in
response.
client caches it and uses this to revalidate using IfNone-Match tag in request header.
GET
/users/23
HTTP/1.1
If-‐None-‐Match:
"23432423423454654667444"
Server
veriQies
the
hash,
if
it
matches
sends
“304”
else
sends
current
value
with
response
code
200
and
resets
the
etag.
35. JAX-RS support Validation
JAX-RS also provided one injectable helper class Request, which has methods
like…
....
ResponseBuilder evalutatePostConditions(EntityTag eTag);
ResponseBuilder evaluatePreConditions(Date isLastModified);
.....
And...
JAX-RS provides us with javax.ws.rs.core.EntityTag for the same
The values sent by client (which they have cached) are compared with latest
values at the server.
36. JAX-RS and Validation
@Path("{id}")
@GET
@Produces(MediaType.APPLICATION_XML)public Response getUserWithEtagSupport(@PathParam
("id") Long id,
@Context Request request){
User user = userDB.get(id);
//generating Etag out of hashCode of user
EntityTag tag = new EntityTag(Integer.toString(user.hashCode()));
CacheControl cc = new CacheControl();
cc.setMaxAge(1000);
ResponseBuilder builder = request.evaluatePreconditions(tag);
if(builder!=null){
//means the preconditions have been met and the cache is valid
//we just need to reset the cachecontrol max age (optional)
builder.cacheControl(cc);
return builder.build();
}
//preconditions are not met and the cache is invalid
//need to send new value with response code 200 (OK)
builder = Response.ok(user,MediaType.APPLICATION_XML);
//reset cache control and eTag (mandatory)
builder.cacheControl(cc);
builder.tag(tag); return builder.build();
}
37. HTTP PURGE
HTTP has an unofficial PURGE method that is used for
purging caches.
When an API receives a call with an unsafe method on
a resource, it should fire a PURGE request on that
resource so that the reverse proxy knows that the
cached resource should be expired.
We dont need to perform explicit revalidations in this
case.
38. GET /article/1234 HTTP/1.1 - The resource is not cached yet
- Send request to the API
- Store response in cache and return
GET /article/1234 HTTP/1.1
- The resource is cached
- Return response from cache
PUT /article/1234 HTTP/1.1
- Unsafe method, send to API
PURGE /article/1234 HTTP/1.1
- API sends PURGE method to the cache
- The resources is removed from the cache
GET /article/1234 HTTP/1.1
- The resource is not cached yet
- Send request to the API - Store response in cache and return
40. GET
-No side effects- should not change the state
-idempotent
HTTP1.1 GET /pitaji/patloon/12/length?
method=decrease&size=1b
Caching will not work!
41. PUT
- idempotent
- HTTP1.1 PUT /pitaji/patloon/12/length
{“decrease” : “1 bilaank” }
This will result in disaster, as the browser can call the
PUT multiple times, in case of timeouts/network
latency etc.
44. Use Case
An example of a social Site :
1.) Add friend
2.) Remove Friend
3.) Approve Friend Request
4.) Reject Friend Request
5.) Make a new account
6.) Delete account.
7.) Search Users.
…...
45. Approach 1 : userFriendMapping table
@Entity
@Table(name = "userFriendMapping")public class UserFriendMapping {
private long id;
private User user;
private User friend;
private String status;
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public long getId() {
return id;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="userId")
public User getUser() {
……
46. Add and Approve friend request
1. Add a friend (send friendRequest)
POST ../userfriendmapping
{userId: 1,
friendId : 2,
status:pending}
2. Approve friend Request
POST ../userfriendmapping
{userId: 1,
friendId : 2,
status:approved}
48. More extensions
List all friend requests
List all pending friends..
List all friends..
List all rejected requests..
Do not allow a user to resend the friend request..
BlackList Users
Ignore a friend request
49. Problems
Single domain catering to responsibilty of two states :
1.) FriendRequest
2.) UserFriendRelation
Increases complexity, more effort, tightly coupled,
separation of concern?
50. 1. Separate domains give more flexibility and ease for
extensibility.
2. As we have states and resources as domains, making
RESTful urls is easy.
3. Querying is easy.
example :
- to find friends need 2 calls to DB, or put a UNION
51. API : Find all myfriends
@Override
public List<User> findFriends(Long userId,String status) {
List<UserFriendMapping> allFriends =
userFriendMappingPersistence.getAllFriends(userId,status);
List<UserFriendMapping> friendsWhoAddedMe =
userFriendMappingPersistence.getByFriendId(userId,status);
List<User> friends = new ArrayList<User>();
for (UserFriendMapping userFriendMapping : allFriends) {
friends.add(userFriendMapping.getFriend());
}
for (UserFriendMapping userFriendMapping : friendsWhoAddedMe)
{
friends.add(userFriendMapping.getUser());
}
return friends;
}
52. Resource Oriented Architecture
A resource-oriented architecture is the structural design
supporting the internetworking of resources.
A resource, in this context, is any entity that can be
identified and assigned a uniform resource identifier (URI).
any states , verbs which acts as a resource
can be made model like FriendRequest or
BookOrder.
53. Alternate Approach
Model driven Architecture and Resource Driven Architecture.
provides intuitive way of designing APIs in RESTful manner.
Add 2 domain classes
● FriendRequest
● UserFriend or FriendShip or Relation
The RESTful APIs :
1. add Friend
POST ../users/1/friendrequests?friendid=2
@Path("/users/{id}/friendrequests")
@POST
public String createFriendRequest(@PathParam("id") Long userId,
@QueryParam(value="friendid")Long friendId){
…...
54. Approve and Reject friendRequest
2. Approve:
POST .. /userfriends/friendrequests/22 -> creating a new friend from
friendRequest with id22
3.Reject
DELETE ../friendrequests/22
4.Remove a friend
DELETE ../userfriends/3
5. GET on ..users/2/friendrequests will give all pending friend requests
6. GET on ..users/1/userfriends/ will give all friends of user
55. Search Users
Search is GET
USE GET with QUERY PARAMS
HTTP1.1 GET ../users?firstname=abc&age=25
56. Versioning APIs in REST
Add version in URL
GET ../version/users/1
Example twitter:
GET https://api.twitter.com/1/statuses/
user_timeline.json
57. Use HTTP Redirection Response codes for versioning
• 301 Moved permanently - point to new URL
• 302 Found indicating that the requested resource temporarily is located
at another location, while requested URI may still supported.
58. Model Driven Design produces RESTful Urls
RAD tools which generate code like Spring ROO or
Rails/Grails.
These are made on top of domains and models.
Take business domains from framework to other.
More extensibility and portability.
and of course they provide RESTful URLs.
59. Finally, Is it Just to avoid verbs and have better
Urls?
The approach should be the other way :
Better modelling and better design gives way to better
URLs and cleaner approach.
60. Conclusion
REST is no specification, its a style which adheres to
HTTP specification.
So, in order to make full use of HTTP and REST
--- Better modelling will automatically avoid verbs.
--- Take care of idempotent and safe/unsafe methods.
--- Use cache-control headers to make best use of
caching.