Modelling RESTful applications – Why should I not use verbs in REST url


Published on

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.

Published in: Technology
1 Like
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Modelling RESTful applications – Why should I not use verbs in REST url

  1. 1. Modelling RESTful applications – Why should I not use verbs in REST url Anirudh Bhatnagar Xebia India IT Architects Pvt Ltd
  2. 2. SOFTWARE DEVELOPMENT DONE RIGHT Netherlands | USA | India | France | UK; Blog :
  3. 3. REST Representational state transfer ?? Specification ??? Guidelines ??? Architecture style??
  4. 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. 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
  6. 6. REST, HTTP and Web
  7. 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. 8. But what about other verbs??? --- approve -- reject -- cancel -- search -- increase -- decrease ….. …..
  9. 9. Can I just put them in my URL?? what would go wrong? Lets see a video
  10. 10. Improper handling of method and no safeguard
  11. 11. Result : Disaster!!!
  12. 12. How do we solve this? HTTP Specifications
  13. 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. 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. 15. Fault Tolerant - Idempotent Methods - if request timed out - can you safely retry? - no need to worry if idempotent
  16. 16. Browser Support Confirm Form submission On refresh of Unsafe Method (POST form)
  17. 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. 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. 19. Bad Urls hamper caching! HTTP 1.1 GET http://myTestApp/ dau22.oid=5199&UserCtxParam=0&GroupCtxParam= 0&dctx1=25&ctx1=US&crc=712082047
  20. 20. HTTP Caching
  21. 21. Browser Caches
  22. 22. Proxy Cache example : Squid
  23. 23. Gateway Cache : Reverse Proxy
  24. 24. Benefits of HTTP Caching - Server side caching is expensive.. - Reduce latency - Reduce network traffic -CDNs can leverage proxy caches.
  25. 25. Leverage Caching effectively With great power comes great responsibility... How to control caching effectively? Invalidations? Cache expiry? Stale cache? Volatile data?
  26. 26. HTTP headers - expires - cache control -Etags -last modified - validation headers
  27. 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. 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; }
  29. 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. 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. 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; }
  32. 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. 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. 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. 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 for the same The values sent by client (which they have cached) are compared with latest values at the server.
  36. 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; } //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; }
  37. 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. 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
  39. 39. Let’s complete our “pitaji ki patloon” problem
  40. 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. 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.
  42. 42. DELETE HTTP/1.1 DELETE /pitaji/patloon/12/length {“decrease” : “1 bilaank” } this API does not make sense, it will confuse the client! moreover again performing unsafe operation with safe method.
  43. 43. POST Unsafe method HTTP1.1 POST /pitaji/patloon/length {“decrease” : “1 bilaank” }
  44. 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. 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. 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}
  47. 47. reject friend, get pending requests 3. Reject friend Request DELETE ../userfriendmapping/1 4.Get pending friends GET ../userfriendmapping/users/1?status=pending 5. Delete existing Friend DELETE ../userfriendmapping/2
  48. 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. 49. Problems Single domain catering to responsibilty of two states : 1.) FriendRequest 2.) UserFriendRelation Increases complexity, more effort, tightly coupled, separation of concern?
  50. 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. 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. 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. 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. 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. 55. Search Users Search is GET USE GET with QUERY PARAMS HTTP1.1 GET ../users?firstname=abc&age=25
  56. 56. Versioning APIs in REST Add version in URL GET ../version/users/1 Example twitter: GET user_timeline.json
  57. 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. 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. 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. 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.
  61. 61. Thanks!!! Questions and Feedback. twitter : anirudh_bh blog : mail : github:
  62. 62. References
  63. 63. Continuous Integration and Delivery Consulting Products Cloud, EC2, Cloud foundation - Deployit Monitoring/logging/ integration Networks, End to end automation vagrant, virtual-box ,lxc,docker, vm - XL Release ü  Automated Build ü  Automated Deployments ü  Automated provisioning of infrastructure ü  Automated Tests Build automation -Jenkins/ Hudson, Linux packaging Infra as Code –chef / puppet Virtualization, SSH, Shell scripting
  64. 64. Contact us @ Websites Xebia India Thought Leadership Htto://