A route handler is a public method annotated with @Route. The value of the annotation is the path the handler answers, relative to the root of the app:
public class Hello {
@Route("/hello")
public String hello() {
return "Hello!";
}
}
app.scan(new Hello());A handler answers exactly one HTTP method. It is GET unless one of these
annotations says otherwise: @Post, @Put, @Patch, @Delete, @Head,
@Options.
@Route("/items")
public String list() { ... }
@Route("/items")
@Post
public String create(Form form) { ... }Two handlers can share a path if they answer different methods.
A GET handler does not answer HEAD requests. A path that must answer HEAD
needs a handler of its own with @Head, see Responses.
A segment starting with : is a variable. Its value is passed to the handler
as a String or int argument:
@Route("/users/:name/orders/:id")
public String order(String name, int id) {
return name + " " + id;
}Variables are bound to parameters in order, not by name. The number of
unannotated String and int parameters must match the number of
variables; scan() fails otherwise. Other kinds of parameters, such as Request or Query, can
appear anywhere in the list (see Handler arguments).
Only int is supported, not Integer. A value that is not a number, e.g.
/users/bob/orders/abc, gets a 404: there is no such resource. To answer it
otherwise, take a String and parse it yourself.
A last segment starting with * matches the rest of the path, slashes
included:
@Route("/files/*path")
public byte[] file(String path) throws IOException {
if (path.contains(".."))
throw new HttpException(404, "Not found");
return Files.readAllBytes(root.resolve(path));
}/files/a/b.txtgivesa/b.txt.- A trailing slash is kept:
/files/a/givesa/. - A splat needs at least one segment, so
/filesand/files/do not match. Add a separate@Route("/files")handler if they should. - A splat can follow variables (
/env/:id/file/*path), but only as the last segment of the route.
Validate a splat before using it as a file path, as in the example above.
app.serveDir() does this for you when serving files.
- The query string is ignored:
/hello?x=1matches@Route("/hello"). - A trailing slash in the request is ignored:
/hello/matches@Route("/hello"). - A fixed segment takes precedence over a variable: with
/api/listand/api/:id, a request for/api/listgoes to the first one. /and/index.htmlare two distinct routes.- A request matching no route gets a 404, or goes to the unknown page handler if there is one (see Errors and hooks).
scan(obj, prefix) binds the routes of an object under a prefix. This lets
one class serve several paths:
app.scan(new ItemRoutes("books"), "/books");
app.scan(new ItemRoutes("music"), "/music");scan() also picks up the public @Route methods that a class inherits.
scan() checks each handler and throws a ScanException explaining what is
wrong. The mistake is reported at startup rather than on the first request.
It catches:
- more or fewer
String/intparameters than path variables - a splat that is not the last segment of the route
- a return type or parameter that Flak cannot handle without a formatter or a parser (see Responses and JSON)
- an
@OutputFormatnaming a formatter that was not registered - a
@QueryParamof an unsupported type, or with an invalid default value
app.getHandlers() returns every route handler of the app, whatever the
backend. The static files served with serveDir() and serveClasspath() are
not among them. app.getHandler(method, route) looks one up, and fails if there is
none:
app.getHandlers()
.forEach(h -> System.out.println(h.getHttpMethod() + " " + h.getRoute()));
RouteHandler upload = app.getHandler("POST", "/api/import");A RouteHandler provides:
getRoute(): the route exactly as declared, relative to the app, variables included (e.g./users/:name). Prependapp.getPath()for an absolute path.getHttpMethod()getJavaMethod(): the Java method it invokes, whose annotations can be readgetParameters(): what each parameter of the method is bound to, as a RouteParameter: a path variable, a query parameter with its default value, the body, or something else, such as theRequestsetMaxBodySize(): see Request bodiesgetInputParser(),getOutputFormatter(): what reads its body and writes its result, e.g. to tell which handlers speak JSONaddHook(): a hook run before this handler only, see Errors and hooks
flak-util provides a ready-made listing, grouped by path:
System.out.print(new RouteDumper().dumpRoutes(app, new StringBuilder()));