or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

annotation-controllers.mdconfiguration.mdcontent-negotiation.mdexception-handling.mdfunctional-routing.mdindex.mdresource-handling.mdview-rendering.mdwebclient.mdwebsocket.md
tile.json

functional-routing.mddocs/

Functional Routing

Spring WebFlux provides a functional programming model for defining routes and handlers. This approach uses RouterFunction to map requests to HandlerFunction instances, with RequestPredicate for matching criteria. It's an alternative to annotation-based controllers that offers more explicit and composable routing.

Capabilities

Define Routes

The RouterFunctions class is the central entry point for creating routes in the functional programming model.

public class RouterFunctions {
    // Create a route that matches the given predicate with a handler function
    public static <T extends ServerResponse> RouterFunction<T> route(
        RequestPredicate predicate,
        HandlerFunction<T> handlerFunction) { ... }

    // Nest routes under a common predicate
    public static <T extends ServerResponse> RouterFunction<T> nest(
        RequestPredicate predicate,
        RouterFunction<T> routerFunction) { ... }

    // Create route for serving static resources
    public static RouterFunction<ServerResponse> resources(
        String pattern,
        Resource location) { ... }

    // Create route for serving resources with custom lookup function
    public static RouterFunction<ServerResponse> resources(
        Function<ServerRequest, Mono<Resource>> lookupFunction) { ... }

    // Convert RouterFunction to HttpHandler
    public static HttpHandler toHttpHandler(RouterFunction<?> routerFunction) { ... }
    public static HttpHandler toHttpHandler(
        RouterFunction<?> routerFunction,
        HandlerStrategies strategies) { ... }

    // Convert RouterFunction to WebHandler
    public static WebHandler toWebHandler(RouterFunction<?> routerFunction) { ... }
    public static WebHandler toWebHandler(
        RouterFunction<?> routerFunction,
        HandlerStrategies strategies) { ... }

    // Visitor pattern support
    interface Visitor {
        void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction);
        void nested(RequestPredicate predicate, RouterFunction<?> routerFunction);
        void resource(Function<ServerRequest, Mono<Resource>> lookupFunction);
        void attributes(Map<String, Object> attributes);
        void unknown(RouterFunction<?> routerFunction);
    }

    // Builder for composing routes
    interface Builder {
        Builder add(RouterFunction<ServerResponse> routerFunction);

        Builder GET(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder GET(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);

        Builder POST(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder POST(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);

        Builder PUT(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder PUT(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);

        Builder PATCH(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder PATCH(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);

        Builder DELETE(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder DELETE(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);

        Builder HEAD(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder HEAD(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);

        Builder OPTIONS(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder OPTIONS(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);

        Builder route(RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);

        Builder resources(String pattern, Resource location);
        Builder resources(Function<ServerRequest, Mono<Resource>> lookupFunction);

        Builder nest(RequestPredicate predicate, Supplier<RouterFunction<ServerResponse>> routerFunctionSupplier);
        Builder nest(RequestPredicate predicate, Consumer<Builder> builderConsumer);

        Builder path(String pattern, Supplier<RouterFunction<ServerResponse>> routerFunctionSupplier);
        Builder path(String pattern, Consumer<Builder> builderConsumer);

        Builder filter(HandlerFilterFunction<ServerResponse, ServerResponse> filterFunction);

        Builder before(Function<ServerRequest, ServerRequest> requestProcessor);
        Builder after(BiFunction<ServerRequest, ServerResponse, ServerResponse> responseProcessor);

        Builder onError(Predicate<? super Throwable> predicate,
            BiFunction<? super Throwable, ServerRequest, Mono<ServerResponse>> responseProvider);
        Builder onError(Class<? extends Throwable> exceptionType,
            BiFunction<? super Throwable, ServerRequest, Mono<ServerResponse>> responseProvider);

        Builder withAttribute(String name, Object value);
        Builder withAttributes(Consumer<Map<String, Object>> attributesConsumer);

        RouterFunction<ServerResponse> build();
    }
}

Usage:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;

@Configuration
public class RouterConfig {

    @Bean
    public RouterFunction<ServerResponse> routes(UserHandler handler) {
        return route(GET("/users/{id}"), handler::getUser)
            .andRoute(GET("/users"), handler::listUsers)
            .andRoute(POST("/users"), handler::createUser)
            .andRoute(PUT("/users/{id}"), handler::updateUser)
            .andRoute(DELETE("/users/{id}"), handler::deleteUser);
    }

    // Using builder API
    @Bean
    public RouterFunction<ServerResponse> routesWithBuilder(UserHandler handler) {
        return RouterFunctions.route()
            .GET("/users/{id}", handler::getUser)
            .GET("/users", handler::listUsers)
            .POST("/users", handler::createUser)
            .PUT("/users/{id}", handler::updateUser)
            .DELETE("/users/{id}", handler::deleteUser)
            .build();
    }

    // Nested routes
    @Bean
    public RouterFunction<ServerResponse> nestedRoutes(UserHandler handler, OrderHandler orderHandler) {
        return RouterFunctions.route()
            .path("/api", builder -> builder
                .nest(path("/users"), userBuilder -> userBuilder
                    .GET("/{id}", handler::getUser)
                    .GET("", handler::listUsers)
                    .POST("", handler::createUser))
                .nest(path("/orders"), orderBuilder -> orderBuilder
                    .GET("/{id}", orderHandler::getOrder)
                    .POST("", orderHandler::createOrder)))
            .build();
    }
}

Router Function

The RouterFunction interface routes requests to handler functions.

@FunctionalInterface
public interface RouterFunction<T extends ServerResponse> {
    // Route the request, returning a HandlerFunction if matched
    Mono<HandlerFunction<T>> route(ServerRequest request);

    // Combine with another router function
    default RouterFunction<T> and(RouterFunction<T> other) { ... }

    // Add a route
    default RouterFunction<T> andRoute(RequestPredicate predicate, HandlerFunction<T> handlerFunction) { ... }

    // Nest routes under a predicate
    default RouterFunction<T> andNest(RequestPredicate predicate, RouterFunction<T> routerFunction) { ... }

    // Add other router function
    default RouterFunction<T> andOther(RouterFunction<?> other) { ... }

    // Apply filter to all handlers
    default RouterFunction<T> filter(HandlerFilterFunction<T, T> filterFunction) { ... }

    // Add request processing before handler
    default RouterFunction<T> before(Function<ServerRequest, ServerRequest> requestProcessor) { ... }

    // Add response processing after handler
    default RouterFunction<T> after(BiFunction<ServerRequest, T, T> responseProcessor) { ... }

    // Accept visitor for traversing routes
    default void accept(RouterFunctions.Visitor visitor) { ... }

    // Add attributes to route
    default RouterFunction<T> withAttribute(String name, Object value) { ... }
    default RouterFunction<T> withAttributes(Consumer<Map<String, Object>> attributesConsumer) { ... }
}

Handler Function

The HandlerFunction interface handles a server request and returns a server response.

@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
    // Handle the given request
    Mono<T> handle(ServerRequest request);
}

Usage:

import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

public class UserHandler {

    private final UserRepository repository;

    public UserHandler(UserRepository repository) {
        this.repository = repository;
    }

    public Mono<ServerResponse> getUser(ServerRequest request) {
        String id = request.pathVariable("id");
        return repository.findById(id)
            .flatMap(user -> ServerResponse.ok().bodyValue(user))
            .switchIfEmpty(ServerResponse.notFound().build());
    }

    public Mono<ServerResponse> listUsers(ServerRequest request) {
        return ServerResponse.ok().body(repository.findAll(), User.class);
    }

    public Mono<ServerResponse> createUser(ServerRequest request) {
        return request.bodyToMono(User.class)
            .flatMap(repository::save)
            .flatMap(user -> ServerResponse.created(
                URI.create("/users/" + user.getId()))
                .bodyValue(user));
    }

    public Mono<ServerResponse> updateUser(ServerRequest request) {
        String id = request.pathVariable("id");
        return request.bodyToMono(User.class)
            .flatMap(user -> repository.update(id, user))
            .flatMap(user -> ServerResponse.ok().bodyValue(user))
            .switchIfEmpty(ServerResponse.notFound().build());
    }

    public Mono<ServerResponse> deleteUser(ServerRequest request) {
        String id = request.pathVariable("id");
        return repository.deleteById(id)
            .then(ServerResponse.noContent().build());
    }
}

Request Predicates

The RequestPredicates class provides factory methods for creating request predicates.

public class RequestPredicates {
    // Match all requests
    public static RequestPredicate all() { ... }

    // Match HTTP method
    public static RequestPredicate method(HttpMethod httpMethod) { ... }
    public static RequestPredicate methods(HttpMethod... httpMethods) { ... }

    // Match request path
    public static RequestPredicate path(String pattern) { ... }
    public static RequestPredicate pathExtension(String extension) { ... }
    public static RequestPredicate pathExtension(Predicate<String> extensionPredicate) { ... }

    // Match query parameter
    public static RequestPredicate queryParam(String name, Predicate<String> predicate) { ... }
    public static RequestPredicate queryParam(String name, String value) { ... }

    // Match path variable
    public static RequestPredicate pathParam(String name, Predicate<String> predicate) { ... }

    // Match request parameter
    public static RequestPredicate param(String name, String value) { ... }
    public static RequestPredicate param(String name, Predicate<String> predicate) { ... }

    // Match headers
    public static RequestPredicate headers(Predicate<ServerRequest.Headers> headersPredicate) { ... }

    // Match content type
    public static RequestPredicate contentType(MediaType... mediaTypes) { ... }

    // Match accept header
    public static RequestPredicate accept(MediaType... mediaTypes) { ... }

    // Match API version
    public static RequestPredicate version(String version) { ... }

    // HTTP method shortcuts
    public static RequestPredicate GET(String pattern) { ... }
    public static RequestPredicate POST(String pattern) { ... }
    public static RequestPredicate PUT(String pattern) { ... }
    public static RequestPredicate PATCH(String pattern) { ... }
    public static RequestPredicate DELETE(String pattern) { ... }
    public static RequestPredicate HEAD(String pattern) { ... }
    public static RequestPredicate OPTIONS(String pattern) { ... }
}

Request Predicate Interface

@FunctionalInterface
public interface RequestPredicate {
    // Test if this predicate matches the given request
    boolean test(ServerRequest request);

    // Combine this predicate with another using AND
    default RequestPredicate and(RequestPredicate other) { ... }

    // Combine this predicate with another using OR
    default RequestPredicate or(RequestPredicate other) { ... }

    // Negate this predicate
    default RequestPredicate negate() { ... }

    // Optional: Nest another predicate
    default Optional<ServerRequest> nest(ServerRequest request) { ... }

    // Optional: Accept visitor for pattern matching
    default void accept(RouterFunctions.Visitor visitor) { ... }
}

Usage:

import static org.springframework.web.reactive.function.server.RequestPredicates.*;

// Combine predicates
RequestPredicate predicate = GET("/users")
    .and(accept(MediaType.APPLICATION_JSON))
    .and(queryParam("active", "true"));

// Content type matching
RequestPredicate jsonPredicate = contentType(MediaType.APPLICATION_JSON);

// Header matching
RequestPredicate headerPredicate = headers(headers ->
    headers.header("X-Custom-Header").contains("value"));

// Query parameter matching
RequestPredicate queryPredicate = queryParam("sort", sort ->
    sort.equals("asc") || sort.equals("desc"));

Server Request

The ServerRequest interface represents a server-side HTTP request.

public interface ServerRequest {
    // HTTP method
    HttpMethod method();

    // Request URI
    URI uri();

    // Request path
    default String path() { ... }
    default PathContainer pathContainer() { ... }

    // Request headers
    Headers headers();

    // Request cookies
    MultiValueMap<String, HttpCookie> cookies();

    // Remote address
    Optional<InetSocketAddress> remoteAddress();

    // Local address
    Optional<InetSocketAddress> localAddress();

    // Path variables
    Map<String, String> pathVariables();
    String pathVariable(String name);

    // Query parameters
    MultiValueMap<String, String> queryParams();
    default Optional<String> queryParam(String name) { ... }

    // Request parameters (query + form)
    MultiValueMap<String, String> params();
    default Optional<String> param(String name) { ... }

    // Attributes
    Map<String, Object> attributes();
    default Optional<Object> attribute(String name) { ... }

    // Body extraction
    <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor);
    <T> Mono<T> bodyToMono(Class<? extends T> elementClass);
    <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> typeReference);
    <T> Flux<T> bodyToFlux(Class<? extends T> elementClass);
    <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> typeReference);

    // Form data
    Mono<MultiValueMap<String, String>> formData();

    // Multipart data
    Mono<MultiValueMap<String, Part>> multipartData();

    // Principal
    default Mono<? extends Principal> principal() { ... }

    // Session
    default Mono<WebSession> session() { ... }

    // Exchange
    default ServerWebExchange exchange() { ... }

    // Check if modified since
    default Mono<Boolean> checkNotModified(Instant lastModified) { ... }
    default Mono<Boolean> checkNotModified(String etag) { ... }
    default Mono<Boolean> checkNotModified(Instant lastModified, String etag) { ... }

    // Builder for creating ServerRequest
    static Builder from(ServerRequest other) { ... }

    interface Headers {
        List<MediaType> accept();
        List<Charset> acceptCharset();
        List<Locale.LanguageRange> acceptLanguage();
        OptionalLong contentLength();
        Optional<MediaType> contentType();
        InetSocketAddress host();
        List<HttpRange> range();
        List<String> header(String headerName);
        HttpHeaders asHttpHeaders();
    }

    interface Builder {
        Builder method(HttpMethod method);
        Builder uri(URI uri);
        Builder header(String headerName, String... headerValues);
        Builder headers(Consumer<HttpHeaders> headersConsumer);
        Builder cookie(String name, String... values);
        Builder cookies(Consumer<MultiValueMap<String, HttpCookie>> cookiesConsumer);
        Builder body(Flux<DataBuffer> body);
        Builder body(String body);
        Builder attribute(String name, Object value);
        Builder attributes(Consumer<Map<String, Object>> attributesConsumer);
        Mono<ServerRequest> build();
    }
}

Usage:

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    // Extract path variable
    String id = request.pathVariable("id");

    // Extract query parameter
    Optional<String> sort = request.queryParam("sort");

    // Extract header
    List<String> customHeaders = request.headers().header("X-Custom-Header");

    // Extract body
    Mono<User> userMono = request.bodyToMono(User.class);

    // Extract form data
    Mono<MultiValueMap<String, String>> formData = request.formData();

    // Check attributes
    Optional<Object> attr = request.attribute("customAttribute");

    return userMono.flatMap(user -> ServerResponse.ok().bodyValue(user));
}

Server Response

The ServerResponse interface represents a typed server-side HTTP response.

public interface ServerResponse {
    // Response status code
    HttpStatusCode statusCode();

    // Response headers
    HttpHeaders headers();

    // Response cookies
    MultiValueMap<String, ResponseCookie> cookies();

    // Write response to exchange
    Mono<Void> writeTo(ServerWebExchange exchange, Context context);

    // Factory methods for status codes
    static BodyBuilder ok() { ... }
    static BodyBuilder created(URI location) { ... }
    static BodyBuilder accepted() { ... }
    static BodyBuilder noContent() { ... }
    static BodyBuilder badRequest() { ... }
    static BodyBuilder notFound() { ... }
    static BodyBuilder unprocessableEntity() { ... }
    static BodyBuilder status(HttpStatusCode status) { ... }
    static BodyBuilder status(int status) { ... }

    // Redirect responses
    static BodyBuilder seeOther(URI location) { ... }
    static BodyBuilder temporaryRedirect(URI location) { ... }
    static BodyBuilder permanentRedirect(URI location) { ... }

    // Server-Sent Events
    static <T> ServerResponse.SseBuilder sse(Consumer<SseBuilder.SendOperations<T>> consumer) { ... }
    static ServerResponse.SseBuilder sse() { ... }

    // Builder from another response
    static Builder from(ServerResponse other) { ... }

    interface HeadersBuilder<B extends HeadersBuilder<B>> {
        B header(String headerName, String... headerValues);
        B headers(Consumer<HttpHeaders> headersConsumer);
        B cookie(ResponseCookie cookie);
        B cookies(Consumer<MultiValueMap<String, ResponseCookie>> cookiesConsumer);
        B allow(HttpMethod... allowedMethods);
        B allow(Set<HttpMethod> allowedMethods);
        B eTag(String etag);
        B lastModified(ZonedDateTime lastModified);
        B lastModified(Instant lastModified);
        B location(URI location);
        B cacheControl(CacheControl cacheControl);
        B varyBy(String... requestHeaders);
        Mono<ServerResponse> build();
        Mono<ServerResponse> build(Publisher<Void> voidPublisher);
    }

    interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
        BodyBuilder contentLength(long contentLength);
        BodyBuilder contentType(MediaType contentType);

        Mono<ServerResponse> bodyValue(Object body);
        <T, P extends Publisher<T>> Mono<ServerResponse> body(P publisher, Class<T> elementClass);
        <T, P extends Publisher<T>> Mono<ServerResponse> body(P publisher, ParameterizedTypeReference<T> elementTypeRef);
        Mono<ServerResponse> body(BodyInserter<?, ? super ServerHttpResponse> inserter);

        Mono<ServerResponse> render(String name, Object... modelAttributes);
        Mono<ServerResponse> render(String name, Map<String, ?> model);
    }

    interface Context {
        List<HttpMessageWriter<?>> messageWriters();
        List<ViewResolver> viewResolvers();
    }
}

Usage:

// OK response with body
public Mono<ServerResponse> getUser(ServerRequest request) {
    return ServerResponse.ok()
        .contentType(MediaType.APPLICATION_JSON)
        .bodyValue(user);
}

// Created response with location
public Mono<ServerResponse> createUser(ServerRequest request) {
    return request.bodyToMono(User.class)
        .flatMap(repository::save)
        .flatMap(user -> ServerResponse.created(URI.create("/users/" + user.getId()))
            .bodyValue(user));
}

// Not found response
public Mono<ServerResponse> userNotFound() {
    return ServerResponse.notFound().build();
}

// No content response
public Mono<ServerResponse> deleteUser(ServerRequest request) {
    return repository.deleteById(request.pathVariable("id"))
        .then(ServerResponse.noContent().build());
}

// Response with custom headers
public Mono<ServerResponse> withHeaders(ServerRequest request) {
    return ServerResponse.ok()
        .header("X-Custom-Header", "value")
        .cookie(ResponseCookie.from("sessionId", "abc123").build())
        .cacheControl(CacheControl.maxAge(3600, TimeUnit.SECONDS))
        .bodyValue(data);
}

// Render view with model
public Mono<ServerResponse> renderView(ServerRequest request) {
    return ServerResponse.ok()
        .render("userView", "user", user, "timestamp", Instant.now());
}

Entity Response

The EntityResponse interface represents a server response with a typed entity body.

public interface EntityResponse<T> extends ServerResponse {
    // Get the entity
    T entity();

    // Create entity response
    static <T> Builder<T> fromObject(T t) { ... }
    static <T> Builder<T> fromPublisher(Publisher<T> publisher, Class<T> entityClass) { ... }
    static <T> Builder<T> fromPublisher(Publisher<T> publisher, ParameterizedTypeReference<T> entityType) { ... }

    interface Builder<T> {
        Builder<T> status(HttpStatusCode status);
        Builder<T> status(int status);
        Builder<T> cookie(ResponseCookie cookie);
        Builder<T> cookies(Consumer<MultiValueMap<String, ResponseCookie>> cookiesConsumer);
        Builder<T> header(String headerName, String... headerValues);
        Builder<T> headers(Consumer<HttpHeaders> headersConsumer);
        Builder<T> allow(HttpMethod... allowedMethods);
        Builder<T> allow(Set<HttpMethod> allowedMethods);
        Builder<T> contentLength(long contentLength);
        Builder<T> contentType(MediaType contentType);
        Builder<T> eTag(String etag);
        Builder<T> lastModified(ZonedDateTime lastModified);
        Builder<T> lastModified(Instant lastModified);
        Builder<T> location(URI location);
        Builder<T> cacheControl(CacheControl cacheControl);
        Builder<T> varyBy(String... requestHeaders);
        Mono<EntityResponse<T>> build();
    }
}

Rendering Response

The RenderingResponse interface represents a server response for rendering a view.

public interface RenderingResponse extends ServerResponse {
    // Get view name
    String name();

    // Get model
    Map<String, Object> model();

    // Create rendering response
    static Builder create(String name) { ... }

    interface Builder {
        Builder status(HttpStatusCode status);
        Builder status(int status);
        Builder cookie(ResponseCookie cookie);
        Builder cookies(Consumer<MultiValueMap<String, ResponseCookie>> cookiesConsumer);
        Builder header(String headerName, String... headerValues);
        Builder headers(Consumer<HttpHeaders> headersConsumer);
        Builder modelAttribute(Object attribute);
        Builder modelAttribute(String name, Object value);
        Builder modelAttributes(Object... attributes);
        Builder modelAttributes(Map<String, ?> attributes);
        Mono<RenderingResponse> build();
    }
}

Handler Filter Function

The HandlerFilterFunction interface filters handler functions.

@FunctionalInterface
public interface HandlerFilterFunction<T extends ServerResponse, R extends ServerResponse> {
    // Filter the handler function
    Mono<R> filter(ServerRequest request, HandlerFunction<T> next);

    // Chain filters
    default <S extends ServerResponse> HandlerFilterFunction<T, S> andThen(
        HandlerFilterFunction<R, S> after) { ... }
}

Usage:

// Logging filter
HandlerFilterFunction<ServerResponse, ServerResponse> loggingFilter =
    (request, next) -> {
        System.out.println("Request: " + request.method() + " " + request.path());
        return next.handle(request)
            .doOnNext(response -> System.out.println("Response: " + response.statusCode()));
    };

// Authentication filter
HandlerFilterFunction<ServerResponse, ServerResponse> authFilter =
    (request, next) -> {
        Optional<String> authHeader = request.headers().firstHeader("Authorization");
        if (authHeader.isPresent() && isValid(authHeader.get())) {
            return next.handle(request);
        }
        return ServerResponse.status(HttpStatus.UNAUTHORIZED).build();
    };

// Apply filters to routes
RouterFunction<ServerResponse> routes = RouterFunctions.route()
    .GET("/users", handler::listUsers)
    .filter(loggingFilter)
    .filter(authFilter)
    .build();

Handler Strategies

The HandlerStrategies interface provides strategies for handler functions.

public interface HandlerStrategies {
    // Get message readers
    List<HttpMessageReader<?>> messageReaders();

    // Get message writers
    List<HttpMessageWriter<?>> messageWriters();

    // Get view resolvers
    List<ViewResolver> viewResolvers();

    // Create with defaults
    static HandlerStrategies withDefaults() { ... }

    // Create builder
    static Builder builder() { ... }

    // Create builder from existing
    Builder mutate();

    interface Builder {
        Builder codecs(Consumer<ServerCodecConfigurer> consumer);
        Builder defaultCodecs();
        Builder viewResolver(ViewResolver viewResolver);
        Builder exceptionHandler(WebExceptionHandler exceptionHandler);
        Builder webFilter(WebFilter webFilter);
        HandlerStrategies build();
    }
}

Types

Resource Handler Function

public class ResourceHandlerFunction implements HandlerFunction<ServerResponse> {
    public ResourceHandlerFunction(Function<ServerRequest, Mono<Resource>> lookupFunction) { ... }

    @Override
    public Mono<ServerResponse> handle(ServerRequest request) { ... }
}

Resource Lookup Functions

public class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resource>> {
    public PathResourceLookupFunction(String pattern, Resource location) { ... }

    @Override
    public Mono<Resource> apply(ServerRequest request) { ... }
}
public class PredicateResourceLookupFunction implements Function<ServerRequest, Mono<Resource>> {
    public PredicateResourceLookupFunction(RequestPredicate predicate, Resource location) { ... }

    @Override
    public Mono<Resource> apply(ServerRequest request) { ... }
}

Router Function Builder

public final class RouterFunctionBuilder implements RouterFunctions.Builder {
    // All builder methods for composing routes
}

Body Extraction

The BodyExtractor interface and BodyExtractors utility class provide functionality for extracting content from request bodies.

@FunctionalInterface
public interface BodyExtractor<T, M extends ReactiveHttpInputMessage> {
    // Extract from the given input message
    T extract(M inputMessage, Context context);

    interface Context {
        List<HttpMessageReader<?>> messageReaders();
        Optional<ServerHttpResponse> serverResponse();
        Map<String, Object> hints();
    }
}

Factory methods for creating body extractors:

public abstract class BodyExtractors {
    // Extract to Mono
    public static <T> BodyExtractor<Mono<T>, ReactiveHttpInputMessage> toMono(Class<? extends T> elementClass) { ... }
    public static <T> BodyExtractor<Mono<T>, ReactiveHttpInputMessage> toMono(ParameterizedTypeReference<T> elementTypeRef) { ... }

    // Extract to Flux
    public static <T> BodyExtractor<Flux<T>, ReactiveHttpInputMessage> toFlux(Class<? extends T> elementClass) { ... }
    public static <T> BodyExtractor<Flux<T>, ReactiveHttpInputMessage> toFlux(ParameterizedTypeReference<T> typeRef) { ... }

    // Extract form data
    public static BodyExtractor<Mono<MultiValueMap<String, String>>, ReactiveHttpInputMessage> toFormData() { ... }

    // Extract multipart data
    public static BodyExtractor<Mono<MultiValueMap<String, Part>>, ServerHttpRequest> toMultipartData() { ... }

    // Extract parts
    public static BodyExtractor<Flux<Part>, ServerHttpRequest> toParts() { ... }

    // Extract data buffers
    public static BodyExtractor<Flux<DataBuffer>, ReactiveHttpInputMessage> toDataBuffers() { ... }
}

Usage Examples:

// Extract body to Mono
public Mono<ServerResponse> handleRequest(ServerRequest request) {
    Mono<User> userMono = request.body(BodyExtractors.toMono(User.class));
    return userMono.flatMap(user -> ServerResponse.ok().bodyValue(user));
}

// Extract body to Flux
public Mono<ServerResponse> handleStreamRequest(ServerRequest request) {
    Flux<Event> events = request.body(BodyExtractors.toFlux(Event.class));
    return ServerResponse.ok().body(events, Event.class);
}

// Extract form data
public Mono<ServerResponse> handleFormSubmit(ServerRequest request) {
    Mono<MultiValueMap<String, String>> formData = request.body(BodyExtractors.toFormData());
    return formData.flatMap(form -> {
        String username = form.getFirst("username");
        return ServerResponse.ok().bodyValue("Hello, " + username);
    });
}

// Extract multipart data
public Mono<ServerResponse> handleFileUpload(ServerRequest request) {
    Mono<MultiValueMap<String, Part>> multipartData = request.body(BodyExtractors.toMultipartData());
    return multipartData.flatMap(parts -> {
        Part filePart = parts.getFirst("file");
        // Process file
        return ServerResponse.ok().build();
    });
}

Body Insertion

The BodyInserter interface and BodyInserters utility class provide functionality for inserting content into response bodies.

@FunctionalInterface
public interface BodyInserter<T, M extends ReactiveHttpOutputMessage> {
    // Insert into the given output message
    Mono<Void> insert(M outputMessage, Context context);

    interface Context {
        List<HttpMessageWriter<?>> messageWriters();
        Optional<ServerHttpRequest> serverRequest();
        Map<String, Object> hints();
    }
}

Factory methods for creating body inserters:

public abstract class BodyInserters {
    // Empty inserter
    public static <T> BodyInserter<T, ReactiveHttpOutputMessage> empty() { ... }

    // Insert single value
    public static <T> BodyInserter<T, ReactiveHttpOutputMessage> fromValue(T body) { ... }
    public static <T> BodyInserter<T, ReactiveHttpOutputMessage> fromValue(T body, ParameterizedTypeReference<T> bodyType) { ... }

    // Insert from producer (Mono, Flux, or other reactive type)
    public static <T> BodyInserter<T, ReactiveHttpOutputMessage> fromProducer(T producer, Class<?> elementClass) { ... }
    public static <T> BodyInserter<T, ReactiveHttpOutputMessage> fromProducer(T producer, ParameterizedTypeReference<?> elementTypeRef) { ... }

    // Insert from Publisher
    public static <T, P extends Publisher<T>> BodyInserter<P, ReactiveHttpOutputMessage> fromPublisher(P publisher, Class<T> elementClass) { ... }
    public static <T, P extends Publisher<T>> BodyInserter<P, ReactiveHttpOutputMessage> fromPublisher(P publisher, ParameterizedTypeReference<T> elementTypeRef) { ... }

    // Insert resource
    public static <T extends Resource> BodyInserter<T, ReactiveHttpOutputMessage> fromResource(T resource) { ... }

    // Insert Server-Sent Events
    public static <T, S extends Publisher<ServerSentEvent<T>>> BodyInserter<S, ServerHttpResponse> fromServerSentEvents(S eventsPublisher) { ... }

    // Insert form data
    public static BodyInserter<MultiValueMap<String, String>, ClientHttpRequest> fromFormData(MultiValueMap<String, String> formData) { ... }

    // Insert multipart data
    public static BodyInserter<MultiValueMap<String, ?>, ClientHttpRequest> fromMultipartData(MultiValueMap<String, ?> multipartData) { ... }

    // Insert data buffers
    public static BodyInserter<Publisher<DataBuffer>, ReactiveHttpOutputMessage> fromDataBuffers(Publisher<DataBuffer> dataBuffers) { ... }
}

Usage Examples:

// Insert single value
public Mono<ServerResponse> respondWithUser(User user) {
    return ServerResponse.ok()
        .body(BodyInserters.fromValue(user));
}

// Insert from Publisher
public Mono<ServerResponse> respondWithStream(Flux<Event> events) {
    return ServerResponse.ok()
        .body(BodyInserters.fromPublisher(events, Event.class));
}

// Insert resource
public Mono<ServerResponse> serveFile(Resource fileResource) {
    return ServerResponse.ok()
        .contentType(MediaType.APPLICATION_OCTET_STREAM)
        .body(BodyInserters.fromResource(fileResource));
}

// Insert Server-Sent Events
public Mono<ServerResponse> streamEvents(ServerRequest request) {
    Flux<ServerSentEvent<String>> events = Flux.interval(Duration.ofSeconds(1))
        .map(seq -> ServerSentEvent.builder("Event " + seq)
            .id(String.valueOf(seq))
            .event("message")
            .build());

    return ServerResponse.ok()
        .contentType(MediaType.TEXT_EVENT_STREAM)
        .body(BodyInserters.fromServerSentEvents(events));
}

Note: In most cases, you can use the convenience methods like bodyValue() and body() on ServerResponse instead of using BodyInserters directly. These utility classes are primarily useful for custom body processing scenarios or when you need fine-grained control over the serialization process.