docs
Functional programming model for defining routes and handlers as an alternative to annotation-based controllers, providing a lightweight and composable approach to request handling.
Represents a function that handles a request and produces a response.
/**
* Represents a function that handles a ServerRequest.
*
* @param <T> the type of the response of the function
*/
@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
/**
* Handle the given request.
*
* @param request the request to handle
* @return the response
* @throws Exception if an error occurs during handling
*/
T handle(ServerRequest request) throws Exception;
}Represents a function that routes to a handler function.
/**
* Represents a function that routes to a handler function.
*
* @param <T> the type of the response of the handler function
*/
@FunctionalInterface
public interface RouterFunction<T extends ServerResponse> {
/**
* Return the handler function that matches the given request.
*
* @param request the request to route
* @return an Optional describing the HandlerFunction that matches this request,
* or an empty Optional if there is no match
*/
Optional<HandlerFunction<T>> route(ServerRequest request);
/**
* Return a composed routing function that first invokes this function,
* and then invokes the given function if this route had no result.
*
* @param other the function to apply when this function has no result
* @return a composed function
*/
default RouterFunction<T> and(RouterFunction<T> other) {}
/**
* Return a composed routing function that routes to the given handler function
* if this route does not match and the given request predicate applies.
*
* @param predicate the predicate to test
* @param handlerFunction the handler function to route to
* @return a composed function
*/
default RouterFunction<T> andRoute(RequestPredicate predicate,
HandlerFunction<T> handlerFunction) {}
/**
* Filter all handler functions routed by this function with the given filter function.
*
* @param filterFunction the filter to apply
* @param <S> the filter return type
* @return the filtered routing function
*/
default <S extends ServerResponse> RouterFunction<S> filter(
HandlerFilterFunction<T, S> filterFunction) {}
}Represents an HTTP request in functional style.
/**
* Represents a server-side HTTP request.
*/
public interface ServerRequest {
/**
* Get the HTTP method.
*
* @return the HTTP method as an HttpMethod enum value
*/
HttpMethod method();
/**
* Get the request URI.
*
* @return the URI of the request
*/
URI uri();
/**
* Get the headers of this request.
*
* @return the headers
*/
ServerRequest.Headers headers();
/**
* Extract the body as an object of the given type.
*
* @param bodyType the class of the body
* @param <T> the body type
* @return the body
* @throws IOException if an I/O error occurs
*/
<T> T body(Class<T> bodyType) throws IOException;
/**
* Extract the body as an object of the given type.
*
* @param bodyType the type of the body
* @param <T> the body type
* @return the body
* @throws IOException if an I/O error occurs
*/
<T> T body(ParameterizedTypeReference<T> bodyType) throws IOException;
/**
* Get the request parameter value for the given name, if present.
*
* @param name the parameter name
* @return the parameter value
*/
Optional<String> param(String name);
/**
* Get all parameters for this request.
*
* @return a map of all parameter names to parameter values
*/
MultiValueMap<String, String> params();
/**
* Get the path variable with the given name, if present.
*
* @param name the variable name
* @return the variable value
*/
String pathVariable(String name);
/**
* Get all path variables for this request.
*
* @return a map of all path variable names to path variable values
*/
Map<String, String> pathVariables();
/**
* Get the cookies of this request.
*
* @return a map of cookie names to cookie values
*/
MultiValueMap<String, Cookie> cookies();
/**
* Get the session associated with the request.
*
* @return the session
*/
Optional<ServerRequest.Session> session();
/**
* Get the remote address to which this request is connected, if available.
*
* @return the remote address
*/
Optional<InetSocketAddress> remoteAddress();
/**
* Get the header values for the given header name, if any.
*
* @param headerName the header name
* @return the list of header values, or an empty list
*/
List<String> header(String headerName);
}Represents an HTTP response in functional style.
/**
* Represents a server-side HTTP response.
*/
public interface ServerResponse {
/**
* Return the status code of this response.
*
* @return the status as an HttpStatusCode value
*/
HttpStatusCode statusCode();
/**
* Return the headers of this response.
*
* @return the headers
*/
HttpHeaders headers();
/**
* Return the cookies of this response.
*
* @return the cookies
*/
MultiValueMap<String, Cookie> cookies();
/**
* Create a builder with the status set to 200 OK.
*
* @return the created builder
*/
static BodyBuilder ok() {}
/**
* Create a builder with a CREATED status and a location header
* set to the given URI.
*
* @param location the location URI
* @return the created builder
*/
static BodyBuilder created(URI location) {}
/**
* Create a builder with an ACCEPTED status.
*
* @return the created builder
*/
static BodyBuilder accepted() {}
/**
* Create a builder with a NO_CONTENT status.
*
* @return the created builder
*/
static HeadersBuilder<?> noContent() {}
/**
* Create a builder with a BAD_REQUEST status.
*
* @return the created builder
*/
static BodyBuilder badRequest() {}
/**
* Create a builder with a NOT_FOUND status.
*
* @return the created builder
*/
static HeadersBuilder<?> notFound() {}
/**
* Create a builder with the given status.
*
* @param status the response status
* @return the created builder
*/
static BodyBuilder status(HttpStatusCode status) {}
/**
* Create a builder with the given status.
*
* @param status the response status
* @return the created builder
*/
static BodyBuilder status(int status) {}
/**
* Defines a builder that adds headers to the response.
*
* @param <B> the builder subclass
*/
interface HeadersBuilder<B extends HeadersBuilder<B>> {
B header(String headerName, String... headerValues);
B headers(Consumer<HttpHeaders> headersConsumer);
B cookie(Cookie cookie);
B cookies(Consumer<MultiValueMap<String, Cookie>> cookiesConsumer);
B allow(HttpMethod... allowedMethods);
B eTag(String etag);
B lastModified(Instant lastModified);
B location(URI location);
B cacheControl(CacheControl cacheControl);
B varyBy(String... requestHeaders);
ServerResponse build();
}
/**
* Defines a builder that adds a body to the response.
*/
interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
BodyBuilder contentLength(long contentLength);
BodyBuilder contentType(MediaType contentType);
ServerResponse body(Object body);
ServerResponse body(Object body, ParameterizedTypeReference<?> bodyType);
}
}Utility class for creating RouterFunction instances.
/**
* Central class for creating RouterFunction instances through the builder-style
* RouterFunctions.route() method.
*/
public abstract class RouterFunctions {
/**
* Route to the given handler function if the given request predicate applies.
*
* @param predicate the predicate to test
* @param handlerFunction the handler function to route to
* @param <T> the type of response returned by the handler function
* @return a router function that routes to handlerFunction if predicate evaluates to true
*/
public static <T extends ServerResponse> RouterFunction<T> route(
RequestPredicate predicate, HandlerFunction<T> handlerFunction) {}
/**
* Create a RouterFunctions.Builder to build a RouterFunction.
*
* @return the builder
*/
public static RouterFunctions.Builder route() {}
/**
* Defines a builder for creating a composed RouterFunction.
*/
public interface Builder {
Builder GET(String pattern, HandlerFunction<ServerResponse> handlerFunction);
Builder GET(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);
Builder HEAD(String pattern, 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 DELETE(String pattern, HandlerFunction<ServerResponse> handlerFunction);
Builder OPTIONS(String pattern, HandlerFunction<ServerResponse> handlerFunction);
Builder route(RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);
Builder add(RouterFunction<ServerResponse> routerFunction);
Builder filter(HandlerFilterFunction<ServerResponse, ServerResponse> filterFunction);
Builder before(Function<ServerRequest, ServerRequest> requestProcessor);
Builder after(BiFunction<ServerRequest, ServerResponse, ServerResponse> responseProcessor);
Builder onError(Predicate<Throwable> predicate, BiFunction<Throwable, ServerRequest, ServerResponse> responseProvider);
RouterFunction<ServerResponse> build();
}
}Usage Example:
@Configuration
public class RouterConfig {
@Bean
public RouterFunction<ServerResponse> productRoutes(ProductHandler handler) {
return RouterFunctions.route()
.GET("/api/products", handler::getAllProducts)
.GET("/api/products/{id}", handler::getProductById)
.POST("/api/products", handler::createProduct)
.PUT("/api/products/{id}", handler::updateProduct)
.DELETE("/api/products/{id}", handler::deleteProduct)
.filter((request, next) -> {
log.info("Request: {} {}", request.method(), request.path());
return next.handle(request);
})
.onError(IllegalArgumentException.class, (error, request) ->
ServerResponse.badRequest().body(error.getMessage()))
.build();
}
}
@Component
public class ProductHandler {
public ServerResponse getAllProducts(ServerRequest request) {
List<Product> products = productService.findAll();
return ServerResponse.ok().body(products);
}
public ServerResponse getProductById(ServerRequest request) {
Long id = Long.parseLong(request.pathVariable("id"));
Product product = productService.findById(id);
return ServerResponse.ok().body(product);
}
public ServerResponse createProduct(ServerRequest request) throws IOException {
Product product = request.body(Product.class);
Product created = productService.save(product);
URI location = URI.create("/api/products/" + created.getId());
return ServerResponse.created(location).body(created);
}
public ServerResponse updateProduct(ServerRequest request) throws IOException {
Long id = Long.parseLong(request.pathVariable("id"));
Product product = request.body(Product.class);
Product updated = productService.update(id, product);
return ServerResponse.ok().body(updated);
}
public ServerResponse deleteProduct(ServerRequest request) {
Long id = Long.parseLong(request.pathVariable("id"));
productService.delete(id);
return ServerResponse.noContent().build();
}
}Predicate for matching requests.
@FunctionalInterface
public interface RequestPredicate {
boolean test(ServerRequest request);
default RequestPredicate and(RequestPredicate other) {}
default RequestPredicate or(RequestPredicate other) {}
default RequestPredicate negate() {}
}Factory for common request predicates.
public abstract class RequestPredicates {
public static RequestPredicate path(String pattern) {}
public static RequestPredicate method(HttpMethod httpMethod) {}
public static RequestPredicate GET(String pattern) {}
public static RequestPredicate POST(String pattern) {}
public static RequestPredicate PUT(String pattern) {}
public static RequestPredicate DELETE(String pattern) {}
public static RequestPredicate PATCH(String pattern) {}
public static RequestPredicate contentType(MediaType... mediaTypes) {}
public static RequestPredicate accept(MediaType... mediaTypes) {}
public static RequestPredicate param(String name, String value) {}
public static RequestPredicate header(String name, String value) {}
}