Javalinは、KotlinとJavaのための軽量なWebフレームワークだ。デフォルトではシンプルなブロック方式で設計されており、WebSocket、HTTP2、非同期リクエストをサポートする。SparkJavaフレームワークのフォークとして始められたが、JavaScriptフレームワークkoa.jsの影響を受けて、すぐにゼロから書き換えられた。
JavalinはJetty上に構築されており、パフォーマンスは素のJettyコードとほとんど変わらない。クラスを拡張したり、@アノテーションを使用したり、JavaとKotlin用で別々のバージョンをダウンロードしたりする必要もない。
Javaを使用する場合は、以下のような"public static void main
"があればよい。
public static void main(String[] args) {
var app = Javalin.create().start(7000);
app.get("/", ctx -> ctx.result("Hello World"));
}
いくつかの設定をコード例で見てみよう。
var app = Javalin.create(config -> {
config.defaultContentType = "application/json";
config.autogenerateEtags = true;
config.addStaticFiles("/public");
config.asyncRequestTimeout = 10_000L;
config.dynamicGzip = true;
config.enforceSsl = true;
}).routes(() -> {
path("users", () -> {
get(UserController::getAll);
post(UserController::create);
path(":user-id"(() -> {
get(UserController::getOne);
patch(UserController::update);
delete(UserController::delete);
});
ws("events", userController::webSocketEvents);
});
}).start(port);
パスパラメータ、クエリパラメータ、フォームパラメータなどのパラメータの検証は非常に簡単だ。
var myQpStr = ctx.queryParam("my-qp"); // no validation, returns String or null
var myQpInt = ctx.pathParam("my-qp", Integer.class).get(); // returns an Integer or throws
var myQpInt = ctx.formParam("my-qp", Integer.class).check(i -> i > 4).get(); // Integer > 4
// validate two dependent query parameters:
var fromDate = ctx.queryParam("from", Instant.class).get();
var toDate = ctx.queryParam("to", Instant.class)
.check(it -> it.isAfter(fromDate), "'to' has to be after 'from'")
.get();
// validate a json body:
var myObject = ctx.bodyValidator(MyObject.class)
.check(obj -> obj.myObjectProperty == someValue)
.get();
Javalinのもうひとつの興味深い機能は、他のフレームワークにも存在するハンドラだ。Javalinには、beforeハンドラ、endpointハンドラ、afterハンドラ、例外ハンドラ、エラーハンドラが用意されている。
//before handlers
app.before(ctx -> {
// runs before all requests
});
app.before("/path/*", ctx -> {
// runs before request to /path/*
});
//endpoint handlers
app.get("/", ctx -> {
// some code
ctx.json(object);
});
app.get("/hello/*", ctx -> {
// capture all request to sub-paths of /hello/
});
//after handlers
app.after(ctx -> {
// run after all requests
});
app.after("/path/*", ctx -> {
// runs after request to /path/*
});
認証や承認処理を行うために、Javalinには、機能インターフェースとしてAccessManager
が用意されている。開発者が必要に応じて、独自のアクセスマネージャを実装することも可能だ。
// Set the access-manager that Javalin should use:
app.accessManager((handler, ctx, permittedRoles) -> {
MyRole userRole = getUserRole(ctx);
if (permittedRoles.contains(userRole)) {
handler.handle(ctx);
} else {
ctx.status(401).result("Unauthorized");
}
});
Role getUserRole(Context ctx) {
// determine user role based on request
// typically done by inspecting headers
}
enum MyRole implements Role {
ANYONE, ROLE_ONE, ROLE_TWO, ROLE_THREE;
}
app.routes(() -> {
get("/un-secured", ctx -> ctx.result("Hello"), roles(ANYONE));
get("/secured", ctx -> ctx.result("Hello"), roles(ROLE_ONE));
});
バージョン3.0以降では、OpenAPI(Swagger)プラグインも提供されており、OpenAPI 3.0仕様の完全な実装をDSLとアノテーションの両方で利用できる。
OpenAPI DSL:
val addUserDocs = document()
.body()
.result("400")
.result("204")
fun addUserHandler(ctx: Context) {
val user = ctx.body()
UserRepository.addUser(user)
ctx.status(204)
}
OpenAPI annotations:
@OpenApi(
requestBody = OpenApiRequestBody(User::class),
responses = [
OpenApiResponse("400", Unit::class),
OpenApiResponse("201", Unit::class)
]
)
fun addUserHandler(ctx: Context) {
val user = ctx.body()
UserRepository.createUser(user)
ctx.status(201)
}
Javalinアプリケーションのデプロイは、(maven-assembly-plugin
を使って)依存関係のあるjarを作成して、"java -jar filename.jar
"でjarを起動すればよい。Jettyサーバを組み込みで持っているため、アプリケーションサーバは不要だ。
Javalinには教育者専用のページも用意されている。その中では、JavalinにはJettyサーバが組み込まれており、サーブレットコンテナ/アプリケーションサーバを構成する必要なくコーディングを始めることができるので、学生にとってメリットがあることが強調されている。
GraalVMでの実行やKotlin CRUD REST APIなど、一連のチュートリアルも提供されている。チュートリアルページには、その完全な一覧がある。
詳細はドキュメントページを参照して頂きたい。Javalinはmaven経由あるいは手動でmaven centralからダウンロードできる。