cache

Signature

def cache[K](cache: Cache[K, RouteResult], keyer: PartialFunction[RequestContext, K]): Directive0

Description

Wraps its inner Route with caching support using the given CacheCache implementation and the provided keyer function.

The directive tries to serve the request from the given cache and only if not found runs the inner route to generate a new response. A simple cache can be constructed using routeCache constructor.

The directive is implemented in terms of cachingProhibited and alwaysCache. This means that clients can circumvent the cache using a Cache-Control request header. This behavior may not be adequate depending on your backend implementation (i.e how expensive a call circumventing the cache into the backend is). If you want to force all requests to be handled by the cache use the alwaysCache directive instead. In complexer cases, e.g. when the backend can validate that a cached request is still acceptable according to the request Cache-Control header the predefined caching directives may not be sufficient and a custom solution is necessary.

Example

Scala
import akka.http.scaladsl.server.directives.CachingDirectives._
import akka.http.scaladsl.server.RequestContext
import akka.http.scaladsl.model.Uri
import akka.http.scaladsl.model.headers.{ Authorization, `Cache-Control` }
import akka.http.scaladsl.model.headers.CacheDirectives.`no-cache`

//Example keyer for non-authenticated GET requests
val simpleKeyer: PartialFunction[RequestContext, Uri] = {
  val isGet: RequestContext => Boolean = _.request.method == GET
  val isAuthorized: RequestContext => Boolean =
    _.request.headers.exists(_.is(Authorization.lowercaseName))
  val result: PartialFunction[RequestContext, Uri] = {
    case r: RequestContext if isGet(r) && !isAuthorized(r) => r.request.uri
  }
  result
}

// Created outside the route to allow using
// the same cache across multiple calls
val myCache = routeCache[Uri]

var i = 0
val route =
  path("cached") {
    cache(myCache, simpleKeyer) {
      complete {
        i += 1
        i.toString
      }
    }
  }

Get("/cached") ~> route ~> check {
  responseAs[String] shouldEqual "1"
}
// now cached
Get("/cached") ~> route ~> check {
  responseAs[String] shouldEqual "1"
}
// caching prevented
Get("/cached") ~> `Cache-Control`(`no-cache`) ~> route ~> check {
  responseAs[String] shouldEqual "2"
}
Java
import static akka.http.javadsl.server.Directives.complete;
import static akka.http.javadsl.server.Directives.extractUri;
import static akka.http.javadsl.server.Directives.path;
import static akka.http.javadsl.server.PathMatchers.segment;
final CachingSettings cachingSettings = CachingSettings.create(system());
final JavaPartialFunction<RequestContext, Uri> simpleKeyer = new JavaPartialFunction<RequestContext, Uri>() {
  public Uri apply(RequestContext in, boolean isCheck) {
    final HttpRequest request = in.getRequest();
    final boolean isGet = request.method() == HttpMethods.GET;
    final boolean isAuthorized = request.getHeader(Authorization.class).isPresent();

    if (isGet && !isAuthorized)
      return request.getUri();
    else
      throw noMatch();
  }
};

// Created outside the route to allow using
// the same cache across multiple calls
final Cache<Uri, RouteResult> myCache = routeCache(cachingSettings);

final AtomicInteger count = new AtomicInteger(0);
final Route route = path(segment("cached"), () ->
  cache(myCache, simpleKeyer, () ->
    extractUri(uri ->
      complete(String.format("Request for %s @ count %d", uri, count.incrementAndGet()))
    )
  )
);

// tests:
testRoute(route)
  .run(HttpRequest.GET("/cached"))
  .assertEntity("Request for http://example.com/cached @ count 1");

// now cached
testRoute(route)
  .run(HttpRequest.GET("/cached"))
  .assertEntity("Request for http://example.com/cached @ count 1");

// caching prevented
final CacheControl noCache = CacheControl.create(CacheDirectives.NO_CACHE);
testRoute(route).run(HttpRequest.GET("/cached").addHeader(noCache))
  .assertEntity("Request for http://example.com/cached @ count 2");
Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.