jimble

リクエストとレスポンス#

入力を読む#

context.request() から取れます。どこから来た値かで分かれています。

メソッド 中身
bodyPath() パスパラメータ(/users/{id}
bodyQuery() クエリ文字列
bodyForm() application/x-www-form-urlencoded / multipart
bodyJson() JSON の本文
bodyFile() アップロードされたファイル(ファイルアップロード
bodyAll() 上を全部重ねたもの

bodyAll() の重ね順は path → query → form → json です。 あとのものが前を上書きします。

戻り値はどれも DataLinkedHashMap<String,Object> の派生)です。 getString getInt getLong getBoolean getData getDataList などで取り出します。 無いキーは getString なら nullgetInt なら 0getBoolean なら false です (「無い」と「0」は区別できません。区別したいときは getIntObject などの Object 版か isNull(key)。 詳しくは ユーティリティ)。

落とし穴

context.request() から直接は読めません。 RequestData なので context.request().getString("title")コンパイルが通りますが、 本文もクエリも入っていないので null が返ります。 上の表のどれか(ふつうは bodyAll())を通してください。

Data input = context.request().bodyAll();
String title = input.getString("title");

getStringOptional は「無ければ空文字」ですが、その空文字を Data に入れます。 読んだだけでキーが増えるので、JSON にして返す直前やループの中では使わないでください (ユーティリティ)。

入れ子のパラメータ#

.[ ] のどちらでも入れ子にできます。

user.name=taro
user[name]=taro          同じ
items[0].price=100
items[].price=100        末尾に足す

[ ] の中が数字なら添字、空なら追加、それ以外なら名前です。 JSON の本文と混ざったときは、フォームの値が JSON を上書きします。

検証する#

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);

エラーは最初の1件で止めません。 全部集めてから返します。 入力し直す人にとって、1件ずつ言われるのがいちばん困るからです。

ValidationMessages.toMessages(errors) で、項目名 → メッセージの Data になります。 そのまま JSON で返すか、テンプレートに渡します。

ルールの一覧、ルート単位でかける ValidationExecutor、ページングは 検証とページング にまとめてあります。

返す#

context.response().send("text");                   // text/plain
context.response().json("posts", list);            // JSON
context.response().view("blog/posts.jte");         // テンプレート
context.response().redirect("/");                  // 302
context.response().download(file, "報告書.xlsx");  // ダウンロード
context.response().code(201).send();               // 本文なし

json() は組み立てるだけで、送るのは send() です。 複数回 json() を呼ぶと、1つの JSON に足されていきます。

get("/posts", context -> context.response().json("posts", BlogApp.listPosts()));

大きいものを流す#

全部をメモリに載せたくないときは outputStream() を直接使います。

context.response().setResponseHeader("Content-Type", "text/csv; charset=UTF-8");

try (OutputStream out = context.response().outputStream()) {
	// 1行ずつ書く。書いたそばから流れていく
}

進捗を送りたいだけなら SSE のほうが簡単です。

二重送信#

send() を2回呼ぶとエラーになります。 isSent() で送信済みかどうかを見られます。 after フィルタや error ハンドラの中では、これを見てから触ってください。