Quarkus 逆引き集

概要

初めて触るフレームワークなので調べたことをまとまりなく書いていく。

諸元

  • Quarkus: 3.15.1
  • Java: 21

リファレンス

REST API実装編

エンドポイントに共通のパスを付与したい

package org.acme.rest;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

@ApplicationPath("/api")
public static class MyApplication extends Application {}

みたいにやるとすべてのエンドポイントの頭に /api をつけられる。

リクエストパラメタのコンテナクラスにRecordクラスを使いたい(けどできない)

複数のリクエストパラメタをグループするカスタムクラスを実装することができる。これはBeanParamを利用している。

@Path("/cheeses/{type}")
public class Endpoint {
    public static class Parameters {
        @RestPath
        String type;
        // ...snip...
        @RestForm
        String smell;
    }
    @POST
    public String allParams(@BeanParam Parameters parameters) { // <- ココ!
        return parameters.type + "/" + parameters.variant + "/" + parameters.age
            + "/" + parameters.level + "/" + parameters.secretHandshake
            + "/" + parameters.smell;
    }
}

ref: カスタムクラスでパラメータをグループ化

このカスタムクラスをRecordクラスで実装したくなるが、Java Beanの規約に準拠していないため残念ながら使えない様子。

パスパラメタやクエリパラメタの型を正規表現で表したい

@Path("{name}/{age:\\d+}")
@GET
public String personalisedHello(String name, int age) {
    return "Hello " + name + " is your age really " + age + "?";
}

@GET
public String genericHello() {
    return "Hello stranger";
}

{age:\\d+} などとするとパラメタ名を正規表現でパターンマッチングかつint等に変換した状態でメソッドに渡すことができる。 マッチしない場合はフォールバックできるメソッドがあればそちらにいく。

オーバーロードのような動作も実現できるが、意図しない動きに繋がりそうなのでやらないほうがいいと思った。

@Path("{name}/{age:\\d+}")
@GET
public String personalizedHello(String name, int age) {
    return String.format("%s is your age really %d?", name, age);
}

@Path("{name}/{comeFrom}")
@GET
public String personalizedHello(String name, String comeFrom) {
    return String.format("Are %s from %s?", name, comeFrom);
}

レスポンスをJsonで返したい。

quarkus-rest-jackson のextensionを入れるとJSONで自動的に返すようになる。 ref: https://quarkus.io/guides/rest#json-serialisation  

任意のステータスコードとレスポンスで返したい

WebApplicationException(これはJakartaの資産)を継承した各種例外をthrowすると対応するステータスコードになる。

@GET
public String findCheese(String cheese) {
    if(cheese == null)
        // send a 400
        throw new BadRequestException();
    if(!cheese.equals("camembert"))
        // send a 404
        throw new NotFoundException("Unknown cheese: " + cheese);
    return "Camembert is a very nice cheese";
}

ref: - 例外のマッピング - java - WebApplicationException vs Response - Stack Overflow

または、メソッドの型を Response にし、以下のような形で任意のHTTPステータスコードと任意のオブジェクトを返せるが公式ガイドに寄れば↑の実装が最善とのこと。

@Path("{id}")
@GET
public Response getPerson(Long id) {
    var p = new Person(id, "foo", "bar", "Brick Lane");
    return Response.status(Response.Status.NOT_FOUND).entity(p).build();
}

(OpenAPI定義にエラーを反映させる方法は?)

Servlet Filter的なものがほしい

@ServerRequestFilter アノテーションおよび @ServerResponseFilter アノテーションで実装できる。

ref: リクエストまたはレスポンスフィルター

OpenAPIç·¨

OpenAPI定義を生成したい

quarkus-smallrye-openapi 拡張を導入することで、 /q/openapi?format=json からOpenAPI定義を手に入れることができる。

ref: https://ja.quarkus.io/guides/openapi-swaggerui#expose-openapi-specifications

info や tags などを追加で定義したい場合は2つの方法があり、1つは @OpenAPIDefinition アノテーションを利用する方法、もう1つは設定ファイル application.{properties|yaml} を利用する方法。 どっちがいいかは好みか? ついでにSwagger-uiも生える。 http://localhost:8080/q/swagger-ui

ref: アプリケーションレベルのOpenAPIアノテーションの提供

OpenAPI定義から生成されるメソッド名をいい感じにして欲しい

OpenAPI定義から生成される実装は、OperationID を元にメソッド名を決める(ことが仕様?)。メソッド毎に @OperationID アノテーションを利用することもできるが、設定ファイルに以下のように書くとメソッド名がそのまま利用できる。

mp.openapi.extensions.smallrye.operationIdStrategy=METHOD

この辺のOpenAPIの実装は SmallRye というプロジェクトの資産を流用しているっぽいので深掘る場合はそちらか。

ref:

自動テスト系(T.B.D.)

quarkus-junit5 拡張によるテストフレームワークを導入出来るっぽい。BDD味があるのと、QuarkusがWAFであるためエンドポイント単位のテストがスコープっぽい。

その他

構造化ログを出力したい

DataDogやCloud Loggingにログを出すなら、構造化ログにすることが望ましい。 quarkus-logging-json 拡張を導入すると自動的に構造化ログが出力される。細かい設定はガイド記事を見てほしい。

拡張を簡単に探したい

IntelliJを使っている場合はpom.xmlのdependenciesからダイアログを呼び出すことができる。

IntelliJによるQuarkus拡張の編集