I am trying to develop restful API using Jersey.I have GET API's for a particular get operation my GET is taking same time from the same client. Is it possible to cache response? Any pointers is appreciated.
Thanks
Caching in REST APIs POST requests are not cacheable by default but can be made cacheable if either an Expires header or a Cache-Control header with a directive, to explicitly allows caching, is added to the response. Responses to PUT and DELETE requests are not cacheable at all.
Cache-Control Header Indicates that resource is cacheable by any component. Indicates that resource is cacheable only by the client and the server, no intermediary can cache the resource. Indicates that a resource is not cacheable. Indicates the caching is valid up to max-age in seconds.
Caching REST APIs offers so many benefits: It improves response times: When a client repeatedly initiates an API without caching instructions, the API's response time is the same whether or not the data changes or not.
Summary of solutions:
Request as method parameter
Interface:
@Path("myentity")
public interface MyEntityResource
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getMyEntity(@Context final Request request);
}
Implementation:
public class MyEntityResourceImpl implements MyEntityResource
@Override
public Response getMyEntity(final Request request) {
final MyEntity myEntity = ... // load entity
final String eTagValue = ... // calclutate value of ETag
final EntityTag eTag = new EntityTag(eTagValue);
ResponseBuilder responseBuilder = request.evaluatePreconditions(eTag);
if (responseBuilder == null) {
return Response.ok(user).tag(eTag).build();
}
return responseBuilder.build();
}
}
Disadvantages:
implementation detail Request
is exposed
return type Reponse
is generic
missing grammar of return type in WADL
client proxy with unnecessary parameter Request
Request as instance variable
Interface:
@Path("myentity")
public interface MyEntityResource
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getMyEntity();
}
Implementation:
public class MyEntityResourceImpl implements MyEntityResource
@Context
private Request request
@Override
public Response getMyEntity() {
final MyEntity myEntity = ... // load entity
final String eTagValue = ... // calclutate value of ETag
final EntityTag eTag = new EntityTag(eTagValue);
ResponseBuilder responseBuilder = request.evaluatePreconditions(eTag);
if (responseBuilder == null) {
return Response.ok(user).tag(eTag).build();
}
return responseBuilder.build();
}
}
Disadvantages:
return type Reponse
is generic
missing grammar of return type in WADL
dependency injection with @Context
is complicated, see https://stackoverflow.com/questions/33240443
ShallowEtagHeaderFilter as web filter
web.xml:
<filter>
<filter-name>etagFilter</filter-name>
<filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>etagFilter</filter-name>
<url-pattern>/api/*</url-pattern>
</filter-mapping>
Interface:
@Path("myentity")
public interface MyEntityResource
@GET
@Produces(MediaType.APPLICATION_JSON)
public MyEntity getMyEntity();
}
Implementation:
public class MyEntityResourceImpl implements MyEntityResource
@Override
public MyEntity getMyEntity() {
final MyEntity myEntity = ... // load entity
return myEntity;
}
}
Disadvantages:
bad server performance, see JavaDoc
works only on uncommitted response
no support of weak ETag
Custom WriterInterceptor as JAX-RS Interceptor
Interceptor:
public class CustomInterceptor implements WriterInterceptor {
@Context
private Request request;
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
OutputStream old = context.getOutputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
context.setOutputStream(buffer);
context.proceed();
byte[] entity = buffer.toByteArray();
String etag = ... // calclutate value of ETag
context.getHeaders().putSingle(HttpHeaders.ETAG, etag);
ResponseBuilder responseBuilder = request.evaluatePreconditions(eTag);
if (responseBuilder == null) {
throw new WebApplicationException(responseBuilder.status(Response.Status.NOT_MODIFIED).header(HttpHeaders.ETAG, etag).build());
}
old.write(entity);
} finally {
context.setOutputStream(old);
}
}
}
See also: ServerCacheInterceptor (Resteasy)
Interface:
@Path("myentity")
public interface MyEntityResource
@GET
@Produces(MediaType.APPLICATION_JSON)
public MyEntity getMyEntity();
}
Implementation:
public class MyEntityResourceImpl implements MyEntityResource
@Override
public MyEntity getMyEntity() {
final MyEntity myEntity = ... // load entity
return myEntity;
}
}
Disadvantages:
no predefined interceptor for Jersey available
bad server performance
no support of weak ETag
ugly workaround with WebApplicationException
Recently I've been solving a similar (if not the same) problem. As a side result of it the following library emerged: https://github.com/AndreyLebedenko/dropwizard-caching-filter
The way it can be used is like below:
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/cached")
@ResponseCachedByFilter(10000)
public Object getCached() {
return dao.get();
}
Hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With