Custom Directives
Part of the power of akka-http directives comes from the ease with which it’s possible to define custom directives at differing levels of abstraction.
There are essentially three ways of creating custom directives:
- By introducing new “labels” for configurations of existing directives
- By transforming existing directives
- By writing a directive “from scratch”
Configuration Labeling
The easiest way to create a custom directive is to simply assign a new name for a certain configuration of one or more existing directives. In fact, most of the predefined akka-http directives can be considered named configurations of more low-level directives.
The basic technique is explained in the chapter about Composing Directives, where, for example, a new directive getOrPut
is defined like this:
public Route getOrPut(Supplier<Route> inner) {
return get(inner).orElse(put(inner));
}
Route route = getOrPut(() -> complete("ok"));
Multiple directives can be nested to produce a single directive out of multiple like this:
// the composed custom directive
/**
* @param authenticate A function returns a set of roles for the credentials of a user
* @param inner Inner route to execute if the provided credentials has the given role
* if not, the request is completed with a
*/
public Route headerBasedAuth(Function<MyCredentials, Set<MyRole>> authenticate, MyRole requiredRole, Supplier<Route> inner) {
return headerValueByName("X-My-User-Id", (userId) -> {
return headerValueByName("X-My-User-Secret", (secret) -> {
Set<MyRole> userRoles = authenticate.apply(new MyCredentials(userId, secret));
if (userRoles.contains(requiredRole)) {
return inner.get();
} else {
return complete(StatusCodes.FORBIDDEN, "Role " + requiredRole + " required for access");
}
});
});
}
// a function for authentication
Function<MyCredentials, Set<MyRole>> authLogic =
(credentials) -> {
if (credentials.userId.equals("admin") && credentials.safeSecretVerification("secret"))
return new HashSet<>(Arrays.asList(MyRole.USER, MyRole.ADMIN));
else
return Collections.emptySet();
};
// and then using the custom route
Route route = get(() ->
path("admin", () ->
headerBasedAuth(authLogic, MyRole.ADMIN, () -> complete(StatusCodes.OK, "admin stuff"))
)
);
Another example is the MethodDirectives which are simply instances of a preconfigured method directive. The low-level directives that most often form the basis of higher-level “named configuration” directives are grouped together in the BasicDirectives trait.