1.5. Functional Endpoints
Spring WebFluxには、軽量な関数型プログラミングモデルがあり、関数ではルーティング・リクエスト処理とイミュータブルな設計を行います。アノテーションベースとは別のプログラミングモデルですが、基礎部分で動作するReactive Spring Webは同一です。
1.5.1. HandlerFunction
HTTPリクエストはHandlerFunction
で処理し、これは基本的にはServerRequest
を受け取ってMono<ServerResponse>
を返す関数です。アノテーションベースを知っているユーザ向けに言えば、ハンドラファクションは@RequestMapping
メソッドに相当します。
ServerRequest
とServerResponse
はイミュータブルなインタフェースで、基底のHTTPメッセージにJDK-8らしいやり方でアクセスを提供します。アクセスはReactive Streamsのノンブロッキングバックプレッシャで行います。リクエストはReactorのFlux
もしくはMono
でボディを公開し、レスポンスはボディとしてReactive StreamsのPublisher
をアクセプトします。これの合理性についてはReactive Librariesで解説します。
ServerRequest
はHTTPリクエストの各種要素、メソッド・URI・クエリパラメータ・ヘッダー(ServerRequest.Headers
経由)、へのアクセスを提供します。ボディへのアクセスはbody
メソッドで行います。たとえば、以下はリクエストボディをMono<String>
に抽出します。
Mono<String> string = request.bodyToMono(String.class);
また、以下はボディをFlux
に抽出する方法で、Person
はボディのコンテンツからデシリアライズするクラスです(つまりボディがJSON, JAXB(XMLの場合)を含む場合Person
クラスはJacksonが処理する)。
Flux<Person> people = request.bodyToFlux(Person.class);
上述のbodyToMono
とbodyToFlux
はジェネリックなServerRequest.body(BodyExtractor)
メソッドを使用する便利なメソッドです。BodyExtractor
は関数型のストラテジインタフェースで、自前の抽出ロジックを書くものですが、一般的なBodyExtractor
のインスタンスは```BodyExtractors````ユーティリティクラスにあります。よって、上記サンプルは以下のようにも書けます。
Mono<String> string = request.body(BodyExtractors.toMono(String.class);
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class);
同様に、ServerResponse
はHTTPレスポンスへのアクセスを提供します。これはイミュータブルなので、ビルダーでServerResponse
を生成します。ビルダーで、レスポンスステータス・レスポンスヘッダーの追加・ボディの指定、が出来ます。たとえば、レスポンスを、200 OKステータス・JSONコンテンツタイプ・ボディ指定、で作るには以下のようにします。
Mono<Person> person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);
また、以下は、201 CREATEDステータス・"Location"
ヘッダー・空のボディ、のレスポンスを作ります。
URI location = ...
ServerResponse.created(location).build();
HandlerFunction
をこれらを使用して作れます。たとえば、以下はごく単純な"Hello World"なラムダのハンドラーで、200 ステータス・ボディにString、のレスポンスを返します。
HandlerFunction<ServerResponse> helloWorld =
request -> ServerResponse.ok().body(fromObject("Hello World"));
上述のように、ラムダでハンドラ関数を簡単に書けますが、場合によっては可読性を損なうので、複数の関数がある場合はメンテナンス性が悪くなります。よって、単一のハンドラかコントローラに、関連するハンドラ関数をグループ化することを推奨します。たとえば、以下はreactiveのPerson
リポジトリを公開するクラスです。
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
public class PersonHandler {
private final PersonRepository repository;
public PersonHandler(PersonRepository repository) {
this.repository = repository;
}
public Mono<ServerResponse> listPeople(ServerRequest request) {
Flux<Person> people = repository.allPeople();
return ServerResponse.ok().contentType(APPLICATION_JSON).body(people, Person.class);
}
public Mono<ServerResponse> createPerson(ServerRequest request) {
Mono<Person> person = request.bodyToMono(Person.class);
return ServerResponse.ok().build(repository.savePerson(person));
}
public Mono<ServerResponse> getPerson(ServerRequest request) {
int personId = Integer.valueOf(request.pathVariable("id"));
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
Mono<Person> personMono = repository.getPerson(personId);
return personMono
.flatMap(person -> ServerResponse.ok().contentType(APPLICATION_JSON).body(fromObject(person)))
.switchIfEmpty(notFound);
}
}
listPeople
はJSONでリポジトリのすべてのPersion
オブジェクトを返します。
createPerson
はリクエストボディ内のPersion
を新規作成します。PersonRepository.savePerson(Person)
はMono<Void>
を返す点に注意してください。personがリクエストから読み込まれて保存されると、空のMonoは完了シグナルを出します。完了シグナル受信時、つまりPerson
保存完了時、にレスポンスを送信するにはbuild(Publisher<Void>)
を使うことになります。
getPerson
はパス変数にIDを指定して単一のpersonを返すハンドラー関数です。リポジトリからPerson
を取得し、もし存在すればJSONレスポンスを生成します。無ければ、switchIfEmpty(Mono<T>)
で404 Not Foundレスポンスを返しています。
1.5.2. RouterFunction
リクエストはRouterFunction
がハンドラー関数にルーティングします。RouterFunction
の関数はServerRequest
を受け取ってMono<HandlerFunction>
を返します。リクエストがある特定のルーティングにマッチするとハンドラ関数が返され、マッチしない場合は空のMono
が返されます。アノテーションベースを知っているユーザ向けに言えば、RouterFunction
は@RequestMapping
アノテーションに相当します。
基本的には、ルータ―関数を自身で書くことはありませんが、リクエスト述語とハンドラ関数でルータ関数を生成するにはRouterFunctions.route(RequestPredicate, HandlerFunction)
を使います。述語が適用可能な場合、そのリクエストはハンドラー関数にルーティングが行われ、そうでない場合ルーティングは実行されず、404 Not Foundレスポンスが返されます。自前のRequestPredicate
を書くことは可能ですが、そうする必要は無く、良くある一般的な述語はRequestPredicates
ユーティリティクラスにあり、例えば、パスペース・HTTPメソッド・コンテンツタイプなどでのマッチングです。route
を使用する、"Hello World"なハンドラ―関数へのルーティングの記述は以下のようになります。
RouterFunction<ServerResponse> helloWorldRoute =
RouterFunctions.route(RequestPredicates.path("/hello-world"),
request -> Response.ok().body(fromObject("Hello World")));
二つのルータ関数を一つの複合ルータ関数にまとめることが出来ます。最初のルーティングの述語にマッチしない場合、二つ目が評価されます。複合ルータ関数はそれぞれ順に評価されるので、狭い述語を先に置き、より汎用の述語を後に置きます。RouterFunction.and(RouterFunction)
かRouterFunction.andRoute(RequestPredicate, HandlerFunction)
で二つのルータ関数を複合化します。後者はRouterFunction.and()
とRouterFunctions.route()
を組み合わせて一度にやるものです。
上述したPersonHandler
を例にとると、個々のハンドラへのルータ関数は以下のように定義します。ハンドラ関数の参照はメソッド参照を使用しています。
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
PersonRepository repository = ...
PersonHandler handler = new PersonHandler(repository);
RouterFunction<ServerResponse> personRoute =
route(GET("/person/{id}").and(accept(APPLICATION_JSON)), handler::getPerson)
.andRoute(GET("/person").and(accept(APPLICATION_JSON)), handler::listPeople)
.andRoute(POST("/person").and(contentType(APPLICATION_JSON)), handler::createPerson);
ルータ関数では、RequestPredicate.and(RequestPredicate)
かRequestPredicate.or(RequestPredicate)
で、リクエスト述語の組み合わせを指定しています。これらの動作は見た目ままで、and
は与えられた述語が両方ともマッチした場合で、or
はどちらかです。RequestPredicates
の述語の多くが複合になっています。たとえば、RequestPredicates.GET(String)
はRequestPredicates.method(HttpMethod)
とRequestPredicates.path(String)
の複合です。
1.5.3. Running a server
HTTPサーバでのルータ関数の動かし方について。最もシンプルなやり方はルータ関数を以下のいずれかの手段でHttpHandler
に変換します。
RouterFunctions.toHttpHandler(RouterFunction)
RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)
戻り値のHttpHandler
は各種サーバ固有のアダプタで使用可能で、以下のHttpHandlerに示す通りです。
別の方法として、WebFlux Configを使用するDispatcherHandlerベースのセットアップで動かす方法があります。WebFlux Configはリクエスト処理に必要となるコンポーネントを宣言するのにSpring configurationを使用します。WebFlux Java configは以下のファンクショナルエンドポイント用の基盤コンポーネントを宣言します。
RouterFunctionMapping
- Spring configurationで一つ以上のRouterFunction<?>
を検出すると、それらをRouterFunction.andOther
で複合化し、出来たRouterFunction
にリクエストをルーティングする。
HandlerFunctionAdapter
- リクエストにマッピングされているHandlerFunction
をDispatcherHandler
が呼び出すためのシンプルなアダプタ。
ServerResponseResultHandler
- HandlerFunction
の呼び出し結果をServerResponse
のwriteTo
を呼び出すことで処理する。
上述のコンポーネントはDispatcherHandler
のリクエスト処理ライフサイクルにファンクショナルエンドポイントをフィットさせるためのもので、同時に、アノテーションのコントローラがもし存在すればこれと並存させるためのものでもあります。また、Spring Boot WebFlux starterがファンクショナルエンドポイントを有効化する方法でもあります。
以下はWebFlux Java configの例です。(動かし方はDispatcherHandler参照)
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Bean
public RouterFunction<?> routerFunctionA() {
}
@Bean
public RouterFunction<?> routerFunctionB() {
}
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
}
@Override
default void addCorsMappings(CorsRegistry registry) {
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
}
}
1.5.4. HandlerFilterFunction
ルータ関数でマッピングしたルーティングはRouterFunction.filter(HandlerFilterFunction)
でフィルタをかけられます。HandlerFilterFunction
はServerRequest
とHandlerFunction
を受け取りServerResponse
を返す関数です。このハンドラ関数の引数はチェーンの次の要素で、基本的にはルーティング先のHandlerFunction
ですが、複数のフィルタが適用される場合は別のFilterFunction
になる場合があります。アノテーションベースの類似機能は@ControllerAdvice
and/or ServletFilter
で実現できます。いま、ルーティングにセキュリティフィルタを追加するとして、特定のパスが許可されるかどうかを判定するSecurityManager
が既にあるとします。
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
SecurityManager securityManager = ...
RouterFunction<ServerResponse> route = ...
RouterFunction<ServerResponse> filteredRoute =
route.filter((request, next) -> {
if (securityManager.allowAccessTo(request.path())) {
return next.handle(request);
}
else {
return ServerResponse.status(UNAUTHORIZED).build();
}
});
上記の例にあるようにnext.handle(ServerRequest)
は必ずしも呼ぶ必要はありません。上記ではアクセスを許可する場合にだけハンドラ関数を呼び出しています。
ファンクショナルエンドポイントでのCORSはCorsWebFilterで行っています。
1.6. CORS
Spring MVCと同等
1.6.1. Introduction
Spring MVCと同等
セキュリティ上の理由によりブラウザはオリジンサーバ外のリソースへのAJAX呼び出しを禁止しています。あるタブで銀行アカウントを取得すると別タブのevil.comでも取得出来てしまいます。最初のタブで取得したクレデンシャルを使用するAJAXリクエストを、二つ目のタブのevil.comのスクリプトで利用可能にさせるできではありません。
Cross-Origin Resource Sharing (CORS)はたいていのブラウザが実装しているW3C specificationで、IFRAMEやJSOPなどのセキュリティが低く強力というわけでもない回避策よりも、許可するクロスドメインリクエストの種類を指定可能になります。
1.6.2. Processing
Spring MVCと同等
CORS仕様ではpreflight, simple, actual requestsを区別しています。CORSの動作を知るにはこの記事や、他にもいろいろありますが、詳細は仕様を参照してください。
Spring WebFluxのHandlerMapping
はCORSのビルトイン機能です。リクエストからハンドラのマッピング正常終了後、HandlerMapping
はリクエスト・ハンドラ・以降のアクションにおけるCORS configurationをチェックします。Preflight requestsは直接処理されますが、simpleとactual CORSリクエストは intercepted, validated, and have required CORS response headers set.
クロスオリジンリクエスト(Origin
ヘッダが存在してリクエストのホストが異なる)の有効化には、明示的なCORS configurations宣言が必要です。CORS configurationが見つからない場合、preflight requestsを拒否します。simpleおよびactual CORSリクエストのレスポンスにCORSヘッダーを追加しないと、以降のブラウザはそれらを拒否します。
HandlerMapping
をCorsConfiguration
マッピングベースのURLパターンで個々に設定できます。たいていの場合はアプリケーションはそうしたマッピングを宣言するのにWebFlux Java configを使います。この場合、すべてのHadlerMappping
に渡される単一のグローバルマッピングになります。
HandlerMapping
のグローバルなCORS configurationはより細かい、ハンドラレベルのCORS configurationと組み合わせることが出来ます。たとえば、アノテーションのコントローラはクラスかメソッドレベルの@CrossOrigin
を使えます(その他のハンドラではCorsConfigurationSource
を実装する)。
グローバルとローカルのconfigurationの組み合わせの際のルールは基本的にはadditiveです。allowCredentials
とmaxAge
など単一値のみ取る属性では、ローカルがグローバルをオーバーライドします。詳細はCorsConfiguration#combine(CorsConfiguration)を参照。
各機能の詳細やより細かいカスタマイズについては以下を参照してください。
CorsConfiguration
CorsProcessor
, DefaultCorsProcessor
AbstractHandlerMapping
1.6.3. @CrossOrigin
Spring MVCと同等
@CrossOriginによりコントローラーのメソッドでクロスオリジンリクエストを有効化します。
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@GetMapping("/{id}")
public Mono<Account> retrieve(@PathVariable Long id) {
}
@DeleteMapping("/{id}")
public Mono<Void> remove(@PathVariable Long id) {
}
}
デフォルトの@CrossOrigin
は以下を許可します。
- すべてのオリジン。
- すべてのヘッダー。
- コントローラーメソッドにマッピングされているすべてのHTTPメソッド
allowedCredentials
はデフォルトでは無効化で、これはクッキーとCSRFトークンなど重要なユーザ固有情報を公開するtrust levelを確立するためです。よって必要な場合にだけ使用すべきです。
maxAge
30分。
クラスレベルの@CrossOrigin
はすべてのメソッドに引き継がれます。
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@GetMapping("/{id}")
public Mono<Account> retrieve(@PathVariable Long id) {
}
@DeleteMapping("/{id}")
public Mono<Void> remove(@PathVariable Long id) {
}
}
クラスとメソッド両方でCrossOrigin
を使用可能です。
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("http://domain2.com")
@GetMapping("/{id}")
public Mono<Account> retrieve(@PathVariable Long id) {
}
@DeleteMapping("/{id}")
public Mono<Void> remove(@PathVariable Long id) {
}
}
1.6.4. Global Config
Spring MVCと同等
コントローラのメソッドレベルに加えて、グローバルのCORS configurationを定義したい場合もあります。HandlerMapping
にURLベースのCorsConfiguration
マッピングを設定できます。ただし、たいていのアプリケーションではWebFlux Java configを使うことになると思われます。
デフォルトのグローバルconfigurationは以下を有効化します。
- すべてのオリジン。
- すべてのヘッダー。
GET
, HEAD
, POST
のメソッド。
allowedCredentials
はデフォルトでは無効化で、これはクッキーとCSRFトークンなど重要なユーザ固有情報を公開するtrust levelを確立するためです。よって必要な場合にだけ使用すべきです。
maxAge
30分。
WebFlux Java configでCORSを有効化するにはCorsRegistry
コールバックを使います。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600);
}
}
1.6.5. CORS WebFilter
Spring MVCと同等
ビルトインのCorsWebFilterでCORSを使用可能で、ファンクショナルエンドポイントにはこちらがよりフィットします。
フィルタの設定にはCorsWebFilter
beanを宣言してそのコンストラクタにCorsConfigurationSource
を渡します。
@Bean
CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("");
config.addAllowedMethod("");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
1.7. Web Security
Spring MVCと同等
Spring Securityは悪意のある攻撃からwebアプリケーションを保護する機能を提供します。詳細はSpring Security reference documentationを参照してください。
1.8. WebFlux Config
Spring MVCと同等
WebFlux Java configはコントローラやファンクショナルエンドポイントでのリクエスト処理に必要となるコンポーネントを宣言しており、カスタマイズのAPIを提供します。Java configが生成するbeanを理解する必要は必ずしもありませんが、必要になった場合は、WebFluxConfigurationSupport
を見たりSpecial bean typesを参照してください。
configuration APIでは利用できない、より高度なカスタマイズについては、Advanced config modeでフルコントロールを得られます。
1.8.1. Enable WebFlux config
Spring MVCと同等
Java configで@EnableWebFlux
を使用します。
@Configuration
@EnableWebFlux
public class WebConfig {
}
上記により多数のSpring WebFlux infrastructure beansを登録します。また、クラスパス上で利用可能な(JSONやXML用などの)依存性に適したbeanも登録します。
1.8.2. WebFlux config API
Spring MVCと同等
Java configでWebFluxConfigurer
インタフェースを実装します。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
}
1.8.3. Conversion, formatting
Spring MVCと同等
デフォルトではNumber
とDate
用のフォーマッターがあり、これらでは@NumberFormat
と@DateTimeFormat
が使えます。また、Joda Timeがクラスパス上にある場合、Joda Timeも使えるようになります。
カスタムのフォーマッターとコンバータを登録するには以下のようにします。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
}
}
FormatterRegistrarsを使うにはFormatterRegistrar SPIとFormattingConversionServiceFactoryBean
を参照してください。
1.8.4. Validation
Spring MVCと同等
デフォルトでは、Bean Validation、Hibernate Validatorなど、がクラスパス上にあればグローバルのValidatorにLocalValidatorFactoryBean
を登録し、@Controller
メソッド引数の@Valid
とValidated
で使われます。
Java configで、グローバルのValidator
インスタンスをカスタマイズできます。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public Validator getValidator(); {
}
}
なお、ローカルにValidator
を登録することもできます。
@Controller
public class MyController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new FooValidator());
}
}
どこかでLocalValidatorFactoryBean
をインジェクションする必要が生じる場合、beanを生成して@Primary
を付与し、MVC configで宣言しているものとのコンフリクトを回避してください。
1.8.5. Content type resolvers
Spring MVCと同等
Spring WebFluxでは@Controller
が要求するメディアタイプの決定方法を設定可能です。デフォルトでは"Accept"ヘッダーのみチェックしますが、クエリ―パラメータベースに変更することも可能です。
コンテンツタイプのリゾルバをカスタマイズするには以下のようにします。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureContentTypeResolver(RequestedContentTypeResolverBuilder builder) {
}
}
1.8.6. HTTP message codecs
Spring MVCと同等
リクエストとレスポンスの読み書きをカスタマイズできます。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
}
}
ServerCodecConfigurer
がデフォルトのリーダーとライターを提供します。これを使用して、リーダー・ライターを追加したり、デフォルトをカスタマイズしたり、あるいは、デフォルトを完全に置き換えたりします。
Jackson JSONとXMLでは、JacksonのデフォルトプロパティをカスタマイズするJackson2ObjectMapperBuilderの使用を検討してください。
- DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES をdisabledにする。
- MapperFeature.DEFAULT_VIEW_INCLUSIONをdisabledにする。
また、以下がクラスパス上にある場合、自動的に登録します。
- jackson-datatype-jdk7:
java.nio.file.Path
などJava 7サポート。
- jackson-datatype-joda: Joda-Timeサポート。
- jackson-datatype-jsr310: Java 8 Date & Time APIサポート
- jackson-datatype-jdk8:
Optional
などJava 8サポート
1.8.7. View resolvers
Spring MVCと同等
viewのリゾルバを設定するには以下のようにします。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
}
}
なお、FreeMarkerではそのライブラリ用の設定が必要です。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("classpath:/templates");
return configurer;
}
}
1.8.8. Static resources
Spring MVCと同等
Resourceのリストで静的リソースを記述します。
以下の例では、"/resources"
で始まるリクエストの場合、クラスパス上の静的リソース"/static"
を参照する相対パスになります。リソースは、HTTPリクエスト削減およびブラウザキャッシュを確保するため、1年の有効期限になります。Last-Modified
ヘッダを評価し、もし存在すれば304
ステータスコードを返します。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public", "classpath:/static/")
.setCachePeriod(31556926);
}
}
リソースハンドラはResourceResolverとResourceTransformerのチェーンが出来ます。which can be used to create a toolchain for working with optimized resources.
VersionResourceResolver
は、コンテンツから算出するMD5・固定アプリケーションバージョンなど、によるバージョン付きリソースURLを使えます。ContentVersionStrategy
(MD5ハッシュ)はモジュールローダーを使うJavaScriptリソースなどで有用な選択肢となります。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public/")
.resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
}
ResourceUrlProvider
でURLリライトを行い、たとえばバージョンを追加するのに、リゾルバとtransformersのチェーンを適用できます。WebFlux configはResourceUrlProvider
を提供するのにでこれを他で使います。
Spring MVCと異なり、今のところのWebFluxでは透過的な静的リソースリライトを行う方法が無く、これはリゾルバとtransformersのノンブロッキングチェーンを利用可能なビュー技術が存在しないためです(Amazon S3上のリソースなど)。ローカルのリソースを提供する場合のみ、直接ResourceUrlProvider
を使います(例えばカスタムタグを経由するなどして)。この場合ブロックは0秒です。
WebJarsはWebJarsResourceResolver
で使用可能で、クラスパス上に"org.webjars:webjars-locator"
がある場合自動的に登録します。このリゾルバはjarのバージョンを含むようにURLをリライトするので、バージョンの無いURLにマッチングします。たとえば、"/jquery/jquery.min.js"
から"/jquery/1.2.0/jquery.min.js"
など。
1.8.9. Path Matching
Spring MVCと同等
Spring WebFluxはパスパターンのパース済み表現、```PathPattern
、を使用します。また、リクエストパスはRequestPath
です。これらにより、リクエストパスのデコードやセミコロン削除をするかどうかを指定する必要を省きます。PathPattern
はデコード済みのパスセグメントにアクセスして安全にマッチングが出来ます。*1
Spring WebFluxはサフィックスパターンマッチングをサポートせず、パスマッチングに関するカスタマイズのオプションは二つだけ存在します。trailing slashesとマッチングするかどうか(デフォルトtrue
)、case-sensitiveとマッチングするかどうか(デフォルトfalse
)。
これらオプションをカスタマイズするには以下のようにします。
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
}
}
1.8.10. Advanced config mode
Spring MVCと同等
@EnableWebFlux
はDelegatingWebFluxConfiguration
をインポートします。これは、(1)WebFluxアプリケーション用のデフォルトのSpring configuration、(2)configurationをカスタマイズするためのWebFluxConfigurer
へのデリゲートの検出、を行います。
advanced modeでは、@EnableWebFlux
を削除し、WebFluxConfigurer
を実装する代わりにDelegatingWebFluxConfiguration
を直接拡張します。
@Configuration
public class WebConfig extends DelegatingWebFluxConfiguration {
}
WebConfig
の既存メソッドはそのままで、ベースクラスのbean宣言をオーバーライドします。また、クラスパス上のその他のWebMvcConfigurer
も使えます。
1.9. HTTP/2
Spring MVCと同等
Servlet 4コンテナはHTTP/2サポートが要求され、Spring Framework 5はServlet API 4と互換性があります。プログラミングモデルの観点からは特にこれといってアプリケーションが何かする必要はありません。サーバ設定に関する考慮事項があります。HTTP/2 wiki pageを参照してください。
現行のSpring WebFluxはNettyのHTTP/2をサポートしません。また、クライアントへのpushing resources programmaticallyもサポートしません。