Request and response#
Reading input#
You get it from context.request(). It is split by where the value came from.
| Method | What is in it |
|---|---|
bodyPath() |
Path parameters (/users/{id}) |
bodyQuery() |
The query string |
bodyForm() |
application/x-www-form-urlencoded / multipart |
bodyJson() |
The JSON body |
bodyFile() |
Uploaded files (File upload) |
bodyAll() |
All of the above, layered together |
bodyAll() layers in this order: path → query → form → json.
Later ones overwrite earlier ones.
Every one of them returns a Data (a subclass of LinkedHashMap<String,Object>).
You pull values out with getString getInt getLong getBoolean getData getDataList and friends.
A missing key gives you null from getString, 0 from getInt, and false from
getBoolean — you cannot tell "missing" from "0". When you need to, use the Object
versions (getIntObject and friends) or isNull(key). See Utilities.
Trap
You cannot read straight off context.request().
Request is a Data too, so context.request().getString("title") compiles — and
returns null, because neither the body nor the query string is in there.
Go through one of the methods in the table above (usually bodyAll()).
Data input = context.request().bodyAll();
String title = input.getString("title");
getStringOptional does give you an empty string when the key is missing — and it
puts that empty string into the Data. Reading alone adds keys, so do not call it
just before serialising to JSON or inside a loop (Utilities).
Nested parameters#
You can nest with either . or [ ].
user.name=taro
user[name]=taro the same
items[0].price=100
items[].price=100 appends to the end
A number inside [ ] is an index, empty means append, anything else is a name.
When form values and a JSON body collide, the form values overwrite the JSON.
Validating#
ValidationRules rules = new ValidationRules()
.put(Item.name, new ValidationRule().empty())
.put(Item.age, new ValidationRule().integer(1, 120));
Data request = new Data();
request.putData(Item.name, "");
request.putData(Item.age, "999");
// エラーは最初の1件で止めず、全部集める(要件 F-V-03)
Data errors = rules.validate(null, request);
Data messages = ValidationMessages.toMessages(errors);
Validation does not stop at the first error. It collects them all, then returns. For the person retyping the form, being told one problem at a time is the worst outcome.
ValidationMessages.toMessages(errors) turns them into a Data of field name → message.
Return that as JSON or hand it to a template.
The list of rules, the ValidationExecutor you can apply per route, and paging
are all in Validation and paging.
Sending a response#
context.response().send("text"); // text/plain
context.response().json("posts", list); // JSON
context.response().view("blog/posts.jte"); // template
context.response().redirect("/"); // 302
context.response().download(file, "report.xlsx"); // download
context.response().code(201).send(); // no body
json() only builds; send() is what sends.
Call json() several times and it keeps adding to the same JSON document.
get("/posts", context -> context.response().json("posts", BlogApp.listPosts()));
Streaming something large#
When you do not want the whole thing in memory, use outputStream() directly.
context.response().setResponseHeader("Content-Type", "text/csv; charset=UTF-8");
try (OutputStream out = context.response().outputStream()) {
// write it row by row; it flows out as you write
}
If all you want is to report progress, SSE is easier.
Sending twice#
Calling send() twice is an error.
isSent() tells you whether the response has already gone out.
Inside an after filter or an error handler, check that before you touch anything.