4.2. REST DSL を使用した REST サービスの定義
REST DSL はファサード
REST DSL は、Java DSL または XML DSL (Domain Specific Language) で REST サービスを定義するための簡略化された構文を提供するファサードです。REST DSL は実際の REST 実装を提供しているわけではなく、既存 の REST 実装 (Apache Camel に複数ある) のラッパーにすぎません。
REST DSL の利点
REST DSL ラッパーレイヤーには、以下の利点があります。
- REST サービスを定義するためのモダンで使いやすい構文である。
- 複数の Apache Camel コンポーネントと互換性がある。
-
OpenAPI インテグレーション (
camel-openapi-java
コンポーネント経由)。
REST DSL と統合可能なコンポーネント
REST DSL は実際の REST 実装ではないため、最初に、ベースとなる実装を提供する Camel コンポーネントを選択する必要があります。現在、以下の Camel コンポーネントが REST DSL に統合されています。
-
Servlet コンポーネント(
camel-servlet
) -
Spark REST コンポーネント (
camel-spark-rest
) -
Netty4 HTTP コンポーネント (
camel-netty4-http
) -
Jetty コンポーネント (
camel-jetty
) -
Restlet コンポーネント (
camel-restlet
) -
Undertow コンポーネント (
camel-undertow
)
REST コンポーネント (camel-core
の一部) は REST 実装ではありません。REST DSL と同様に、REST コンポーネントはファサードであり、URI 構文を使用して REST サービスを定義するための簡潔な構文を提供します。REST コンポーネントには、ベースとなる REST 実装も必要です。
REST 実装を使用するように REST DSL を設定
REST 実装を指定するには、restConfiguration()
ビルダー (Java DSL の場合) または restConfiguration
要素 (XML DSL の場合) を使用します。たとえば、Spark-Rest コンポーネントを使用するように REST DSL を設定するには、Java DSL で以下のようなビルダー式を使用します。
restConfiguration().component("spark-rest").port(9091);
そして、XML DSL では、以下のような要素 (camelContext
の子として) を使用します。
<restConfiguration component="spark-rest" port="9091"/>
構文
REST サービスを定義するための Java DS L 構文は以下のとおりです。
rest("BasePath").Option(). .Verb("Path").Option().[to() | route().CamelRoute.endRest()] .Verb("Path").Option().[to() | route().CamelRoute.endRest()] ... .Verb("Path").Option().[to() | route().CamelRoute];
ここの CamelRoute
は、オプションの組み込み Camel ルートです (標準の Java DSL 構文を使用して定義されています)。
REST サービスの定義は rest()
キーワードで始まり、その後に特定の URL パスセグメントを処理する 1 つ以上の Verb 句が続きます。HTTP 動詞は、get()
、head()
、put()
、post()
、delete()
、patch()
または verb()
のいずれかになります。Verb 句は以下の構文のいずれかを使用できます。
to()
キーワードで終わる Verb 句。以下に例を示します。get("...").Option()+.to("...")
キーワード
route()
で終わる Verb 句 (Camel ルートの埋め込みの場合)。以下に例を示します。get("...").Option()+.route("...").CamelRoute.endRest()
Java による REST DSL
Java で REST DSL でサービスを定義するには、通常の Apache Camel ルート定義と同様に、REST 定義を RouteBuilder.configure()
メソッドのボディーに配置します。たとえば、REST DSL と Spark-Rest コンポーネントの組み合わせを使用して Hello World のサービスを定義するには、以下の Java コードを定義します。
restConfiguration().component("spark-rest").port(9091); rest("/say") .get("/hello").to("direct:hello") .get("/bye").to("direct:bye"); from("direct:hello") .transform().constant("Hello World"); from("direct:bye") .transform().constant("Bye World");
前述の例では、3 種類のビルダーがあります。
restConfiguration()
- 特定の REST 実装 (Spark-Rest) を使用するように REST DSL を設定します。
rest()
-
REST DSL を使用してサービスを定義します。各 Verb 句はキーワード
to()
で終了となります。このキーワードは、受信メッセージをエンドポイントdirect
に転送します (direct
コンポーネントは、同じアプリケーション内でルートをつなぎ合わせます)。 from()
- 通常の Camel ルートを定義します。
XML を使用した REST DSL
XML で XML DSL でサービスを定義するには、rest
要素を camelContext
の子要素として定義します。たとえば、Spark-Rest コンポーネントで REST DSL を使用して単純な Hello World サービスを定義するには、以下の XML コード (Blueprint) を定義します。
<camelContext xmlns="http://camel.apache.org/schema/blueprint"> <restConfiguration component="spark-rest" port="9091"/> <rest path="/say"> <get uri="/hello"> <to uri="direct:hello"/> </get> <get uri="/bye"> <to uri="direct:bye"/> </get> </rest> <route> <from uri="direct:hello"/> <transform> <constant>Hello World</constant> </transform> </route> <route> <from uri="direct:bye"/> <transform> <constant>Bye World</constant> </transform> </route> </camelContext>
ベースパスの指定
rest()
キーワード (Java DSL) または rest
要素の path
属性 (XML DSL) を使用してベースパスを定義できます。このパスはすべての Verb 句のパスに接頭辞として付けられます。たとえば、以下は Java DSL のスニペットを示します。
rest("/say")
.get("/hello").to("direct:hello")
.get("/bye").to("direct:bye");
または、以下は XML DSL のスニペットを示します。
<rest path="/say">
<get uri="/hello">
<to uri="direct:hello"/>
</get>
<get uri="/bye" consumes="application/json">
<to uri="direct:bye"/>
</get>
</rest>
REST DSL ビルダーは、以下の URL マッピングで公開します。
/say/hello /say/bye
ベースパスはオプションです。必要であれば、各 Verb 句にフルパスを指定することもできます。
rest() .get("/say/hello").to("direct:hello") .get("/say/bye").to("direct:bye");
Dynamic To の使用
REST DSL では、toD
動的な to パラメーターをサポートしています。動的な to パラメーターを使用して動的な転送先 URI を指定できます。
たとえば、動的エンドポイント URI を使って動的な JMS キューへ送信するには以下のように定義できます。
public void configure() throws Exception { rest("/say") .get("/hello/{language}").toD("jms:queue:hello-${header.language}"); }
XML DSL では、以下のようになります。
<rest uri="/say"> <get uri="/hello//{language}"> <toD uri="jms:queue:hello-${header.language}"/> </get> <rest>
toD
パラメーターの詳細は、「Dynamic To」 を参照してください。
URI テンプレート
Verb 句で利用する引数では、URI テンプレートで指定できます。これにより、特定のパスセグメントを名前付きプロパティーとして取り込むことができます (これらは Camel メッセージヘッダーにマッピングされます)。たとえば、Hello World アプリケーションをパーソナライズして、発信者の名前で挨拶するようにする場合は、以下のような REST サービスを定義することができます。
rest("/say") .get("/hello/{name}").to("direct:hello") .get("/bye/{name}").to("direct:bye"); from("direct:hello") .transform().simple("Hello ${header.name}"); from("direct:bye") .transform().simple("Bye ${header.name}");
URI テンプレートは {name}
パスセグメントのテキストを取得し、このキャプチャーされたテキストを name
メッセージヘッダーにコピーします。URL が /say/hello/Joe
で終わる GET HTTP リクエストを送信してサービスを呼び出す場合、HTTP レスポンスは Hello Joe
になります。
組み込みルートの構文
to()
キーワード (Java DSL) または to
要素 (XML DSL) で Verb 句を終わらせる代わりに、route()
キーワード (Java DSL) または route
要素 (XML DSL) を使用して Apache Camel ルートを直接 REST DSL に埋め込むことも可能です。route()
キーワードを使用すると、以下の構文でルートを Verb 句に埋め込みできます。
RESTVerbClause.route("...").CamelRoute.endRest()
endRest()
キーワード (Java DSL のみ) は、(rest()
ビルダーに複数の Verb 句がある場合に) Verb 句を区切ることができる必須の句読点です。
たとえば、Hello World の例を Java DSL のように組み込み Camel ルートを使用するようにリファクタリングできます。
rest("/say") .get("/hello").route().transform().constant("Hello World").endRest() .get("/bye").route().transform().constant("Bye World");
XML DSL では以下のようになります。
<camelContext xmlns="http://camel.apache.org/schema/blueprint"> ... <rest path="/say"> <get uri="/hello"> <route> <transform> <constant>Hello World</constant> </transform> </route> </get> <get uri="/bye"> <route> <transform> <constant>Bye World</constant> </transform> </route> </get> </rest> </camelContext>
現在の CamelContext
内で、例外句 (onException()
) やインターセプター (intercept()
) を定義した場合、これらの例外句とインターセプターは組み込みルートでもアクティブになります。
REST DSL と HTTP トランスポートコンポーネント
HTTP コンポーネントを明示的に指定しない場合、REST DSL はクラスパス上の利用可能なコンポーネントをチェックすることで、どの HTTP コンポーネントを使用するかを自動検出します。REST DSL は、HTTP コンポーネントのデフォルト名を探し、最初に見つかったものを使用します。クラスパス上に HTTP コンポーネントがなく、かつ HTTP トランスポートが明示的に設定されていない場合は、デフォルトの HTTP コンポーネントは camel-http
になります。
リクエストとレスポンスのコンテンツタイプの指定
Java の consumes()
と produces()
オプション、または XML の consumes
と produces
属性を使用して、HTTP リクエストとレスポンスのコンテンツタイプをフィルターリングすることができます。たとえば、いくつかの一般的なコンテンツタイプ (正式にはインターネットメディアタイプと呼ばれます) は以下のとおりです。
-
text/plain
-
text/html
-
text/xml
-
application/json
-
application/xml
コンテンツタイプは REST DSL の Verb 句のオプションとして指定されます。たとえば、Verb 句を制限して text/plain
の HTTP リクエストのみを受け付け、text/html
の HTTP レスポンスのみを送信するようにするには、以下のような Java コードを使用します。
rest("/email") .post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo");
XML では、以下のように consumes
および produces
属性を設定できます。
<camelContext xmlns="http://camel.apache.org/schema/blueprint"> ... <rest path="/email"> <post uri="/to/{recipient}" consumes="text/plain" produces="text/html"> <to "direct:foo"/> </get> </rest> </camelContext>
また、consumes()
または produces()
の引数をコンマ区切りのリストとして指定することもできます。たとえば、consumes("text/plain, application/json")
などです。
追加の HTTP メソッド
HTTP サーバーの実装によっては、REST DSL の標準動詞セット (get()
、head()
、put()
、post()
、delete()
、patch()
) では提供されない追加の HTTP メソッドをサポートしているものもあります。追加の HTTP メソッドにアクセスするには、Java DSL の場合は汎用キーワード verb()
、XML DSL の場合は汎用要素 verb
を使用できます。
たとえば、Java DSL の場合、HTTP メソッド TRACE は以下のように実装します。
rest("/say") .verb("TRACE", "/hello").route().transform();
ここで、transform()
は IN メッセージのボディーを OUT メッセージのボディーにコピーし、HTTP リクエストに返答させています。
XML DSL の場合、HTTP メソッド TRACE は以下のように実装します。
<camelContext xmlns="http://camel.apache.org/schema/blueprint"> ... <rest path="/say"> <verb uri="/hello" method="TRACE"> <route> <transform/> </route> </get> </camelContext>
カスタム HTTP エラーメッセージの定義
REST サービスがエラーメッセージを返答する必要がある場合、以下のようにカスタム HTTP エラーメッセージを定義できます。
-
Exchange.HTTP_RESPONSE_CODE
ヘッダーにエラーコードの値を設定して、HTTP エラーコードを指定します (例:400
、404
など)。この設定は、正常時のレスポンスではなく、エラーメッセージをレスポンスする REST DSL を示します。 - メッセージのボディーにカスタムエラーメッセージを設定します。
-
必要に応じて
Content-Type
ヘッダーを設定します。 REST サービスが Java オブジェクトとの間でマーシャリングするように設定されている場合 (
bindingMode
が有効になっている場合)、skipBindingOnErrorCode
オプションが有効になっていることを確認する必要があります (デフォルトは有効)。これは、REST DSL がレスポンスを送信する際にメッセージボディーをアンマーシャリングしないようにするためです。オブジェクトバインディングの詳細については、「Java オブジェクトとの間のマーシャリング」 を参照してください。
以下の Java DSL の例は、カスタムエラーメッセージを定義する方法を示しています。
// Java // Configure the REST DSL, with JSON binding mode restConfiguration().component("restlet").host("localhost").port(portNum).bindingMode(RestBindingMode.json); // Define the service with REST DSL rest("/users/") .post("lives").type(UserPojo.class).outType(CountryPojo.class) .route() .choice() .when().simple("${body.id} < 100") .bean(new UserErrorService(), "idTooLowError") .otherwise() .bean(new UserService(), "livesWhere");
この例では、入力 ID が 100 未満の数値の場合、以下のように実装された UserErrorService
Bean を使用してカスタムエラーメッセージを返します。
// Java public class UserErrorService { public void idTooLowError(Exchange exchange) { exchange.getIn().setBody("id value is too low"); exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "text/plain"); exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400); } }
UserErrorService
Bean では、カスタムエラーメッセージを定義し、HTTP エラーコードを 400
に設定します。
パラメーターのデフォルト値
受信する Camel メッセージのヘッダーにデフォルト値を指定することができます。
たとえば、クエリーパラメーターの verbose
などのキーワードを使用して、デフォルト値を指定することができます。以下のコードではデフォルト値は false
となります。これは、verbose
キーを持つヘッダーに他の値が提供されていない場合、デフォルトが false
となります。
rest("/customers/") .get("/{id}").to("direct:customerDetail") .get("/{id}/orders") .param() .name("verbose") .type(RestParamType.query) .defaultValue("false") .description("Verbose order details") .endParam() .to("direct:customerOrders") .post("/neworder").to("direct:customerNewOrder");
カスタム HTTP エラーメッセージでの JsonParserException のラッピング
カスタムのエラーメッセージを返する場合によくあるのは、JsonParserException
例外をラッピングすることです。例として、以下のように、Camel の例外処理メカニズムを利用して、HTTP エラーコード 400 のカスタム HTTP エラーメッセージを作成することができます。
// Java onException(JsonParseException.class) .handled(true) .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400)) .setHeader(Exchange.CONTENT_TYPE, constant("text/plain")) .setBody().constant("Invalid json data");
REST DSL の他のオプション
一般的に、REST DSL のオプションは、以下のようにサービス定義のベース部分 (rest()
の直後) に直接適用することができます。
rest("/email").consumes("text/plain").produces("text/html")
.post("/to/{recipient}").to("direct:foo")
.get("/for/{username}").to("direct:bar");
この場合、指定したオプションは下位のすべての Verb 句に適用されます。または、以下のように、個々の Verb 句にオプションを適用することもできます。
rest("/email") .post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo") .get("/for/{username}").consumes("text/plain").produces("text/html").to("direct:bar");
この場合、指定したオプションは関連する Verb 句にのみ適用され、ベース部分の設定は上書きされます。
表4.1「REST DSL のオプション」 は、REST DSL でサポートされているオプションをまとめたものです。
Java DSL | XML DSL | 説明 |
---|---|---|
|
|
バインディングモードを指定します。これを使用して、受信メッセージを Java オブジェクトにマーシャリングすることができます (オプションで、Java オブジェクトを送信メッセージにアンマーシャリングすることもできます)。値は |
|
|
HTTP リクエストで指定されたインターネットメディアタイプ (MIME タイプ) のみを受け入れるように Verb 句を制限します。代表的な値は、 |
|
| JMX Management のカスタム ID を指定します。 |
|
| REST サービスまたは Verb 句の説明文を記載します。JMX Management やツールを使う場合に便利です。 |
|
|
|
|
| RES T サービスの一意の ID を指定します。これは、JMX Management や他のツールを使用する際に便利です。 |
|
|
この Verb 句で処理する HTTP メソッドを指定します。通常は一般的な |
|
|
オブジェクトバインディングが有効な場合 ( |
|
|
HTTP レスポンスで指定されたインターネットメディアタイプ (MIME タイプ) のみを生成するように Verb 句を制限します。代表的な値は、 |
|
|
オブジェクトバインディングが有効な場合 ( |
|
|
Verb 句の引数としてパスセグメントまたは URI テンプレートを指定します。たとえば、 |
|
|
|