Spring Web Reactive | 1. Spring WebFlux | 1.6. URIリンク

Web MVC

このセクションでは、URIを準備するためにSpring Frameworkで利用できるさまざまなオプションについて説明する。

1.6.1. UriComponents

Spring MVCとSpring WebFlux

UriComponentsBuilderは、次の例のように変数を持つURIテンプレートからURIを作成できる。

Java

UriComponents uriComponents = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")     // (1) 
        .queryParam("q", "{q}")     // (2) 
        .encode()    // (3) 
        .build();    // (4) 

URI uri = uriComponents.expand("Westin", "123").toUri();     // (5) 

Kotlin

UriComponents uriComponents = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")    // (1) 
        .queryParam("q", "{q}")    // (2) 
        .encode()   // (3) 
        .build();   // (4) 

URI uri = uriComponents.expand("Westin", "123").toUri();     // (5) 
  • (1) URIテンプレートを使用する静的ファクトリメソッド。
  • (2) URIコンポーネントを追加または置換する。
  • (3) URIテンプレートとURI変数をエンコードするよう要求する。
  • (4) UriComponentsをビルドする。
  • (5) 変数を展開し、URIを取得する。

上の例は、次の例のように1つのチェーンにまとめ、buildAndExpandで短縮できる。

Java

URI uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")
        .queryParam("q", "{q}")
        .encode()
        .buildAndExpand("Westin", "123")
        .toUri();

Kotlin

val uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")
        .queryParam("q", "{q}")
        .encode()
        .buildAndExpand("Westin", "123")
        .toUri()

次の例では、URIへ直接移動することで、エンコードが暗黙に行われるため、さらに省略できる。

Java

URI uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")
        .queryParam("q", "{q}")
        .build("Westin", "123");

Kotlin

val uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")
        .queryParam("q", "{q}")
        .build("Westin", "123")

次の例のように、完全なURIテンプレートを使用すればさらに短縮できる。

Java

URI uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")
        .queryParam("q", "{q}")
        .build("Westin", "123");

Kotlin

val uri = UriComponentsBuilder
    .fromUriString("https://example.com/hotels/{hotel}?q={q}")
    .build("Westin", "123")

1.6.2. UriBuilder

Spring MVCとSpring WebFlux

UriComponentsBuilderUriBuilderを実装している。UriBuilderFactoryを使用してUriBuilderを作成できる。UriBuilderFactoryUriBuilderは、ベースURL、エンコード設定、その他の詳細などの共有構成に基づいて、URIテンプレートからURIを構築するプラグ可能なメカニズムを提供する。

RestTemplateWebClientUriBuilderFactoryで構成し、URIの準備をカスタマイズできる。DefaultUriBuilderFactoryは内部的にUriComponentsBuilderを使用し、共有構成オプションを公開するUriBuilderFactoryのデフォルト実装である。

次の例は、RestTemplateを構成する方法を示している。

Java

// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;

String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);

RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);

Kotlin

// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode

val baseUrl = "https://example.org"
val factory = DefaultUriBuilderFactory(baseUrl)
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES

val restTemplate = RestTemplate()
restTemplate.uriTemplateHandler = factory

次の例ではWebClientを構成する。

Java

// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;

String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);

WebClient client = WebClient.builder().uriBuilderFactory(factory).build();

Kotlin

// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode

val baseUrl = "https://example.org"
val factory = DefaultUriBuilderFactory(baseUrl)
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES

val client = WebClient.builder().uriBuilderFactory(factory).build()

また、DefaultUriBuilderFactoryを直接使用することもできる。UriComponentsBuilderの使用と似ているが、次の例のように、静的ファクトリメソッドではなく構成設定を保持する実際のインスタンスである。

Java

String baseUrl = "https://example.com";
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);

URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
        .queryParam("q", "{q}")
        .build("Westin", "123");

Kotlin

val baseUrl = "https://example.com"
val uriBuilderFactory = DefaultUriBuilderFactory(baseUrl)

val uri = uriBuilderFactory.uriString("/hotels/{hotel}")
        .queryParam("q", "{q}")
        .build("Westin", "123")

1.6.3. URIエンコーディング

Spring MVCとSpring WebFlux

UriComponentsBuilderは2つのレベルでエンコードオプションを提供する。

  • UriComponentsBuilder#encode() : まずURIテンプレートを事前にエンコードし、その後、展開時にURI変数を厳密にエンコードする。
  • UriComponents#encode() : URI変数を適用した後にURIコンポーネントをエンコードする。

どちらのオプションも、非ASCII文字と不正な文字をエスケープされたオクテット(octet)に変換する。ただし、最初のオプションはURI変数に現れる予約された意味を持つ文字も置換する。

「;」を考えてみよう。この文字はパスでは有効だが、意味は予約されている。最初のオプションは「;」を置換するため、URI変数には%3Bが含まれるが、URIテンプレートに含まれるものは置換されない。対照的に、2番目のオプションはパスの正当な文字であるため「;」を置換しない。

ほとんどの場合、最初のオプションはURI変数を完全にエンコードされる不透明(opaque)データとして扱うため、望ましい結果が得られる。2番目のオプションは、URI変数に意図的に予約文字が含まれている場合に有用である。2番目のオプションは、URI変数をまったく展開しない場合にも役立つ。これは偶然URI変数のように見えるものもエンコードするためである。

次の例では最初のオプションを使用する。

Java

URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
        .queryParam("q", "{q}")
        .encode()
        .buildAndExpand("New York", "foo+bar")
        .toUri();

// Result is "/hotel%20list/New%20York?q=foo%2Bbar"

Kotlin

val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
        .queryParam("q", "{q}")
        .encode()
        .buildAndExpand("New York", "foo+bar")
        .toUri()

// Result is "/hotel%20list/New%20York?q=foo%2Bbar"

次の例のように、URIへ直接移動することで、上の例を短縮できる。これはエンコードを意味する。

Java

URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
        .queryParam("q", "{q}")
        .build("New York", "foo+bar");

Kotlin

val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
        .queryParam("q", "{q}")
        .build("New York", "foo+bar")

次の例のように、完全なURIテンプレートを使用すればさらに短縮できる。

Java

URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
        .build("New York", "foo+bar");

Kotlin

val uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
        .build("New York", "foo+bar")

WebClientRestTemplateは、UriBuilderFactory戦略によって内部的にURIテンプレートを展開してエンコードする。どちらも次の例のようにカスタム戦略で構成できる。

Java

String baseUrl = "https://example.com";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl)
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);

// Customize the RestTemplate..
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);

// Customize the WebClient..
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();

Kotlin

val baseUrl = "https://example.com"
val factory = DefaultUriBuilderFactory(baseUrl).apply {
    encodingMode = EncodingMode.TEMPLATE_AND_VALUES
}

// Customize the RestTemplate..
val restTemplate = RestTemplate().apply {
    uriTemplateHandler = factory
}

// Customize the WebClient..
val client = WebClient.builder().uriBuilderFactory(factory).build()

DefaultUriBuilderFactory実装は、内部的にUriComponentsBuilderを使用してURIテンプレートを展開してエンコードする。ファクトリとして、次のエンコードモードのいずれかに基づいて、エンコードへのアプローチを構成する単一の場所を提供する。

  • TEMPLATE_AND_VALUES: 前の一覧の最初のオプションに相当するUriComponentsBuilder#encode()を使用し、URIテンプレートを事前にエンコードし、展開時にURI変数を厳密にエンコードする。
  • VALUES_ONLY: URIテンプレートはエンコードせず、代わりにUriUtils#encodeUriVariablesを使用して、URI変数をテンプレートへ展開する前にURI変数へ正確なエンコードを適用する。
  • URI_COMPONENT: 前の一覧の2番目のオプションに相当するUriComponents#encode()を使用し、URI変数が展開された後にURIコンポーネント値をエンコードする。
  • NONE: エンコードは適用されない。

RestTemplateは後方互換性のためEncodingMode.URI_COMPONENTに設定されている。WebClientDefaultUriBuilderFactoryのデフォルト値に依存している。これは5.0.xのEncodingMode.URI_COMPONENTから、5.1のEncodingMode.TEMPLATE_AND_VALUESへ変更された。