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 の consumesproduces 属性を使用して、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 エラーメッセージを定義できます。

  1. Exchange.HTTP_RESPONSE_CODE ヘッダーにエラーコードの値を設定して、HTTP エラーコードを指定します (例: 400404 など)。この設定は、正常時のレスポンスではなく、エラーメッセージをレスポンスする REST DSL を示します。
  2. メッセージのボディーにカスタムエラーメッセージを設定します。
  3. 必要に応じて Content-Type ヘッダーを設定します。
  4. 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 でサポートされているオプションをまとめたものです。

表4.1 REST DSL のオプション

Java DSLXML DSL説明

bindingMode()

@bindingMode

バインディングモードを指定します。これを使用して、受信メッセージを Java オブジェクトにマーシャリングすることができます (オプションで、Java オブジェクトを送信メッセージにアンマーシャリングすることもできます)。値は off (デフォルト)、autojsonxmljson_xml です。

consumes()

@consumes

HTTP リクエストで指定されたインターネットメディアタイプ (MIME タイプ) のみを受け入れるように Verb 句を制限します。代表的な値は、text/plaintext/httptext/xmlapplication/jsonapplication/xml です。

customId()

@customId

JMX Management のカスタム ID を指定します。

description()

description

REST サービスまたは Verb 句の説明文を記載します。JMX Management やツールを使う場合に便利です。

enableCORS()

@enableCORS

true の場合、HTTP レスポンスで CORS (オリジン間リソース共有) ヘッダーを有効にします。デフォルトは false です。

id()

@id

RES T サービスの一意の ID を指定します。これは、JMX Management や他のツールを使用する際に便利です。

method()

@method

この Verb 句で処理する HTTP メソッドを指定します。通常は一般的な verb() キーワードと組み合わせて使用します。

outType()

@outType

オブジェクトバインディングが有効な場合 (bindingMode オプションが有効な場合)、このオプションは HTTP レスポンス メッセージを表す Java 型を指定します。

produces()

produces

HTTP レスポンスで指定されたインターネットメディアタイプ (MIME タイプ) のみを生成するように Verb 句を制限します。代表的な値は、text/plaintext/httptext/xmlapplication/jsonapplication/xml です。

type()

@type

オブジェクトバインディングが有効な場合 (bindingMode オプションが有効な場合)、このオプションは HTTP リクエスト メッセージを表す Java 型を指定します。

VerbURIArgument

@uri

Verb 句の引数としてパスセグメントまたは URI テンプレートを指定します。たとえば、get(VerbURIArgument) です。

BasePathArgument

@path

rest() キーワード (Java DSL) または rest 要素 (XML DSL) でベースパスを指定します。