indic threads delhi13-rest-anirudh
TRANSCRIPT
![Page 1: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/1.jpg)
Modelling RESTful applications – Why should I not use verbs in REST urlAnirudh BhatnagarXebia India IT Architects Pvt Ltd
![Page 2: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/2.jpg)
REST
Representational state transfer ??
Specification ???
Guidelines ???
Architecture style??
![Page 3: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/3.jpg)
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.
![Page 4: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/4.jpg)
Roy Fielding’s paper on REST architectural style
Architectural Styles and the Design of Network-based Software Architectures
DISSERTATIONsubmitted in partial satisfaction of the requirements for the degree ofDOCTOR OF PHILOSOPHYin Information and Computer SciencebyRoy Thomas Fielding2000
![Page 5: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/5.jpg)
REST, HTTP and Web
![Page 6: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/6.jpg)
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.
![Page 7: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/7.jpg)
But what about other verbs???
--- approve-- reject-- cancel-- search -- increase-- decrease…..…..
![Page 8: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/8.jpg)
Can I just put them in my URL??what would go wrong?
Lets see a video
http://www.youtube.com/watch?v=65ilZ8esAUs
![Page 9: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/9.jpg)
Improper handling of method and no safeguard
![Page 10: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/10.jpg)
Result : Disaster!!!
![Page 11: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/11.jpg)
How do we solve this?
HTTP Specifications
![Page 12: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/12.jpg)
1. Safe and Unsafe Methodsexcerpt 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..
![Page 13: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/13.jpg)
2.IdempotencyAn idempotent HTTP method can be called many times without different outcomes.
a = 4 ->idempotenta++ -> 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..
![Page 14: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/14.jpg)
Fault Tolerant - Idempotent Methods - if request timed out - can you safely retry? - no need to worry if idempotent
![Page 15: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/15.jpg)
Browser SupportConfirm Form submission On refresh of Unsafe Method (POST form)
![Page 16: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/16.jpg)
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.
![Page 17: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/17.jpg)
caching with GETEvery 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.
![Page 18: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/18.jpg)
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
![Page 19: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/19.jpg)
HTTP Caching
![Page 20: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/20.jpg)
Browser Caches
![Page 21: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/21.jpg)
Proxy Cacheexample : Squid
![Page 22: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/22.jpg)
Gateway Cache : Reverse Proxy
![Page 23: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/23.jpg)
Benefits of HTTP Caching
- Server side caching is expensive..
- Reduce latency
- Reduce network traffic
-CDNs can leverage proxy caches.
![Page 24: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/24.jpg)
Leverage Caching effectivelyWith great power comes great responsibility...
How to control caching effectively?Invalidations?Cache expiry?Stale cache?Volatile data?
![Page 25: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/25.jpg)
- expires
- cache control -Etags -last modified
- validation headers
HTTP headers
![Page 26: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/26.jpg)
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>
![Page 27: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/27.jpg)
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(); }
![Page 28: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/28.jpg)
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.
![Page 29: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/29.jpg)
Cache-ControlCache-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.
![Page 30: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/30.jpg)
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(); }
![Page 31: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/31.jpg)
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 headersbeyond 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.
![Page 32: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/32.jpg)
Last-Modified and If-Modified-Since Server sends in response header
HTTP/1.1 200 OK....Cache-Control: max-age=1000Last-Modified: Mon, 19 aug 2013 16:00 IST
Client revalidates using conditional GET
GET /users/23 HTTP/1.1If-Modified-Since: Mon, 19 aug 2013 16:00 IST
in case it is modified after this date; a response code 200 (OK) with current value of resource will be sent.
And if the data is not modified a response code of “304″
![Page 33: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/33.jpg)
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 If-
None-Match tag in request header. GET /users/23 HTTP/1.1
If-None-Match: "23432423423454654667444"
Server verifies the hash, if it matches sends “304” else sends current value with response code 200 and resets the etag.
![Page 34: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/34.jpg)
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.
![Page 35: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/35.jpg)
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(); }
![Page 36: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/36.jpg)
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.
![Page 37: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/37.jpg)
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
![Page 38: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/38.jpg)
Let’s complete our “pitaji ki patloon” problem
![Page 39: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/39.jpg)
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!
![Page 40: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/40.jpg)
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.
![Page 41: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/41.jpg)
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.
![Page 42: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/42.jpg)
POST
Unsafe method
HTTP1.1 POST /pitaji/patloon/length {“decrease” : “1 bilaank” }
![Page 43: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/43.jpg)
Use Case
An example of a social Site :
1.) Add friend2.) Remove Friend3.) Approve Friend Request4.) Reject Friend Request5.) Make a new account6.) Delete account.7.) Search Users.…...
![Page 44: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/44.jpg)
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() { ……
![Page 45: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/45.jpg)
Add and Approve friend request
1. Add a friend (send friendRequest)POST ../userfriendmapping{userId: 1,friendId : 2,status:pending}
2. Approve friend RequestPOST ../userfriendmapping{userId: 1,friendId : 2,status:approved}
![Page 46: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/46.jpg)
reject friend, get pending requests
3. Reject friend RequestDELETE ../userfriendmapping/1
4.Get pending friendsGET ../userfriendmapping/users/1?status=pending
5. Delete existing FriendDELETE ../userfriendmapping/2
![Page 47: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/47.jpg)
More extensions
List all friend requestsList all pending friends..List all friends..List all rejected requests..Do not allow a user to resend the friend request..BlackList UsersIgnore a friend request
![Page 48: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/48.jpg)
ProblemsSingle domain catering to responsibilty of two states :
1.) FriendRequest2.) UserFriendRelation
Increases complexity, more effort, tightly coupled, separation of concern?
![Page 49: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/49.jpg)
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
![Page 50: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/50.jpg)
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; }
![Page 51: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/51.jpg)
Resource Oriented ArchitectureA 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.
![Page 52: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/52.jpg)
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 RelationThe RESTful APIs :1. add FriendPOST ../users/1/friendrequests?friendid=2
@Path("/users/{id}/friendrequests") @POST public String createFriendRequest(@PathParam("id") Long userId, @QueryParam(value="friendid")Long friendId){ …...
![Page 53: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/53.jpg)
Approve and Reject friendRequest
2. Approve: POST .. /userfriends/friendrequests/22 -> creating a new friend from friendRequest with id22
3.RejectDELETE ../friendrequests/22
4.Remove a friendDELETE ../userfriends/3
5. GET on ..users/2/friendrequests will give all pending friend requests6. GET on ..users/1/userfriends/ will give all friends of user
![Page 54: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/54.jpg)
Search UsersSearch is GETUSE GET with QUERY PARAMSHTTP1.1 GET ../users?firstname=abc&age=25
![Page 55: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/55.jpg)
Versioning APIs in RESTAdd version in URLGET ../version/users/1
Example twitter: GET https://api.twitter.com/1/statuses/user_timeline.json
![Page 56: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/56.jpg)
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.
![Page 57: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/57.jpg)
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.
![Page 58: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/58.jpg)
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.
![Page 59: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/59.jpg)
ConclusionREST 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.
![Page 60: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/60.jpg)
Thanks!!!
Questions and Feedback.
twitter : anirudh_bhblog : http://anirudhbhatnagar.commail : [email protected]: https://github.com/anirudh83
![Page 61: Indic threads delhi13-rest-anirudh](https://reader034.vdocuments.net/reader034/viewer/2022052618/554f9e97b4c90586258b481d/html5/thumbnails/61.jpg)
Referenceswww.w3.org/Protocols/rfc2616/rfc2616-sec13.htmlhttp://www.w3.org/Protocols/rfc2616/rfc2616-sec9.htmlhttp://javasymposium.techtarget.com/html/images/BBurke_Scaling_JAX-RS.pdfhttp://restcookbook.com/Basics/caching/http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.htmlhttp://www.squid-cache.org/http://odino.org/rest-better-http-cache/