or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdcontroller-annotations.mdfunctional-web.mdindex.mdresource-handling.mdservlet-framework.mdsupport-utilities.mdview-resolution.md

functional-web.mddocs/

0

# Functional Web Framework

1

2

Spring MVC's functional web framework provides a modern, functional programming approach to web application development. It offers an alternative to annotation-based controllers using RouterFunction and HandlerFunction interfaces for composable, type-safe request routing and handling.

3

4

## Capabilities

5

6

### RouterFunction

7

8

The core functional interface that represents a function routing to a handler function. RouterFunctions can be composed, filtered, and nested to create complex routing logic in a functional style.

9

10

```java { .api }

11

/**

12

* Represents a function that routes to a handler function.

13

* @param <T> the type of response returned by the handler function

14

*/

15

@FunctionalInterface

16

public interface RouterFunction<T extends ServerResponse> {

17

18

/**

19

* Return the handler function that matches the given request, if any.

20

* @param request the request to route

21

* @return an Optional containing the handler function, or empty if no match

22

*/

23

Optional<HandlerFunction<T>> route(ServerRequest request);

24

25

/**

26

* Return a composed routing function that first tries this function, then the other.

27

* @param other the router function to apply when this function returns empty

28

* @return a composed router function

29

*/

30

default RouterFunction<T> and(RouterFunction<T> other);

31

32

/**

33

* Return a composed routing function that routes to different response types.

34

* @param other the router function to compose with

35

* @return a composed router function

36

*/

37

default RouterFunction<?> andOther(RouterFunction<?> other);

38

39

/**

40

* Return a composed routing function that tests the given predicate against the request.

41

* @param predicate the predicate to test

42

* @param handlerFunction the handler function to route to if predicate matches

43

* @return a composed router function

44

*/

45

default RouterFunction<T> andRoute(RequestPredicate predicate, HandlerFunction<T> handlerFunction);

46

47

/**

48

* Filter all handler functions routed by this function with the given filter function.

49

* @param filterFunction the filter to apply

50

* @return a filtered router function

51

*/

52

default <S extends ServerResponse> RouterFunction<S> filter(HandlerFilterFunction<T, S> filterFunction);

53

54

/**

55

* Add the given attribute to this router function.

56

* @param name the attribute name

57

* @param value the attribute value

58

* @return a router function with the added attribute

59

*/

60

default RouterFunction<T> withAttribute(String name, Object value);

61

62

/**

63

* Add the given attributes to this router function.

64

* @param attributes the attributes to add

65

* @return a router function with the added attributes

66

*/

67

default RouterFunction<T> withAttributes(Consumer<Map<String, Object>> attributesConsumer);

68

}

69

```

70

71

**Usage Examples:**

72

73

```java

74

@Configuration

75

public class RouterConfig {

76

77

@Bean

78

public RouterFunction<ServerResponse> userRoutes() {

79

return route(GET("/users"), this::getAllUsers)

80

.andRoute(GET("/users/{id}"), this::getUser)

81

.andRoute(POST("/users"), this::createUser)

82

.andRoute(PUT("/users/{id}"), this::updateUser)

83

.andRoute(DELETE("/users/{id}"), this::deleteUser);

84

}

85

86

@Bean

87

public RouterFunction<ServerResponse> productRoutes() {

88

return nest(path("/products"),

89

route(GET(""), this::getAllProducts)

90

.andRoute(GET("/{id}"), this::getProduct)

91

.andRoute(POST(""), this::createProduct)

92

).filter(this::loggingFilter);

93

}

94

95

private ServerResponse getAllUsers(ServerRequest request) {

96

// Implementation

97

return ServerResponse.ok().body(userService.findAll());

98

}

99

}

100

```

101

102

### HandlerFunction

103

104

Functional interface representing a function that handles a server request and returns a server response.

105

106

```java { .api }

107

/**

108

* Represents a function that handles a server request.

109

* @param <T> the type of the response returned by this function

110

*/

111

@FunctionalInterface

112

public interface HandlerFunction<T extends ServerResponse> {

113

114

/**

115

* Handle the given request.

116

* @param request the request to handle

117

* @return the response

118

* @throws Exception if an error occurs during handling

119

*/

120

T handle(ServerRequest request) throws Exception;

121

}

122

```

123

124

**Usage Examples:**

125

126

```java

127

public class UserHandler {

128

129

private final UserService userService;

130

131

public UserHandler(UserService userService) {

132

this.userService = userService;

133

}

134

135

public ServerResponse getAllUsers(ServerRequest request) {

136

String sort = request.queryParam("sort").orElse("name");

137

List<User> users = userService.findAll(sort);

138

return ServerResponse.ok()

139

.contentType(MediaType.APPLICATION_JSON)

140

.body(users);

141

}

142

143

public ServerResponse getUser(ServerRequest request) {

144

Long id = Long.valueOf(request.pathVariable("id"));

145

return userService.findById(id)

146

.map(user -> ServerResponse.ok()

147

.contentType(MediaType.APPLICATION_JSON)

148

.body(user))

149

.orElse(ServerResponse.notFound().build());

150

}

151

152

public ServerResponse createUser(ServerRequest request) throws Exception {

153

User user = request.body(User.class);

154

User savedUser = userService.save(user);

155

URI location = URI.create("/users/" + savedUser.getId());

156

return ServerResponse.created(location)

157

.contentType(MediaType.APPLICATION_JSON)

158

.body(savedUser);

159

}

160

161

public ServerResponse updateUser(ServerRequest request) throws Exception {

162

Long id = Long.valueOf(request.pathVariable("id"));

163

User updates = request.body(User.class);

164

return userService.update(id, updates)

165

.map(user -> ServerResponse.ok()

166

.contentType(MediaType.APPLICATION_JSON)

167

.body(user))

168

.orElse(ServerResponse.notFound().build());

169

}

170

}

171

```

172

173

### ServerRequest

174

175

Represents a server-side HTTP request in the functional web framework, providing access to method, URI, headers, and body.

176

177

```java { .api }

178

/**

179

* Represents a server-side HTTP request, as handled by a HandlerFunction.

180

*/

181

public interface ServerRequest {

182

183

/** Return the HTTP method */

184

HttpMethod method();

185

186

/** Return the HTTP method as a String */

187

String methodName();

188

189

/** Return the request URI */

190

URI uri();

191

192

/** Return the request path */

193

String path();

194

195

/** Return the query parameters */

196

MultiValueMap<String, String> queryParams();

197

198

/** Return a query parameter value */

199

default Optional<String> queryParam(String name);

200

201

/** Return the request headers */

202

ServerRequest.Headers headers();

203

204

/** Return the cookies */

205

MultiValueMap<String, HttpCookie> cookies();

206

207

/** Return the body as the given type */

208

<T> T body(Class<T> bodyType) throws IOException, ServletException;

209

210

/** Return the body as the given ParameterizedTypeReference */

211

<T> T body(ParameterizedTypeReference<T> bodyType) throws IOException, ServletException;

212

213

/** Return a path variable value */

214

default Optional<String> pathVariable(String name);

215

216

/** Return all path variables */

217

Map<String, String> pathVariables();

218

219

/** Return the web session */

220

WebSession session();

221

222

/** Return the principal */

223

default Optional<Principal> principal();

224

225

/** Return request attributes */

226

Map<String, Object> attributes();

227

228

/** Return an attribute value */

229

default Optional<Object> attribute(String name);

230

231

// Static factory methods

232

233

/** Create a ServerRequest based on the given HttpServletRequest */

234

static ServerRequest create(HttpServletRequest request, List<HttpMessageConverter<?>> messageReaders);

235

236

/**

237

* Nested interface for request headers.

238

*/

239

interface Headers {

240

/** Return header values for the given header name */

241

List<String> header(String headerName);

242

243

/** Return the first header value for the given name */

244

default Optional<String> firstHeader(String headerName);

245

246

/** Return the Accept header values */

247

List<MediaType> accept();

248

249

/** Return the Accept-Charset header values */

250

List<Charset> acceptCharset();

251

252

/** Return the Accept-Language header values */

253

List<Locale.LanguageRange> acceptLanguage();

254

255

/** Return the Content-Length header value */

256

OptionalLong contentLength();

257

258

/** Return the Content-Type header value */

259

Optional<MediaType> contentType();

260

261

/** Return all headers as HttpHeaders */

262

HttpHeaders asHttpHeaders();

263

}

264

}

265

```

266

267

**Usage Examples:**

268

269

```java

270

public ServerResponse handleRequest(ServerRequest request) {

271

// Access HTTP method and path

272

HttpMethod method = request.method();

273

String path = request.path();

274

275

// Access query parameters

276

Optional<String> limit = request.queryParam("limit");

277

Optional<String> offset = request.queryParam("offset");

278

279

// Access path variables

280

Optional<String> id = request.pathVariable("id");

281

Map<String, String> pathVars = request.pathVariables();

282

283

// Access headers

284

Optional<String> authHeader = request.headers().firstHeader("Authorization");

285

List<MediaType> acceptTypes = request.headers().accept();

286

287

// Access request body

288

try {

289

User user = request.body(User.class);

290

// Process user

291

} catch (Exception e) {

292

return ServerResponse.badRequest().build();

293

}

294

295

return ServerResponse.ok().build();

296

}

297

```

298

299

### ServerResponse

300

301

Represents a server-side HTTP response in the functional web framework, providing a fluent API for building responses.

302

303

```java { .api }

304

/**

305

* Represents a server-side HTTP response, as returned by a HandlerFunction.

306

*/

307

public interface ServerResponse {

308

309

/** Return the status code of this response */

310

HttpStatusCode statusCode();

311

312

/** Return the headers of this response */

313

HttpHeaders headers();

314

315

/**

316

* Write this response to the given HttpServletResponse.

317

* @param request the current request

318

* @param response the servlet response to write to

319

* @param context the context used for writing

320

* @return a ModelAndView, or null

321

*/

322

ModelAndView writeTo(HttpServletRequest request, HttpServletResponse response, Context context)

323

throws ServletException, IOException;

324

325

// Static factory methods for different response types

326

327

/** Create a response with the given status */

328

static BodyBuilder status(HttpStatusCode status);

329

330

/** Create a response with the given status code */

331

static BodyBuilder status(int status);

332

333

/** Create a 200 OK response */

334

static BodyBuilder ok();

335

336

/** Create a 201 Created response with the given location */

337

static BodyBuilder created(URI location);

338

339

/** Create a 202 Accepted response */

340

static BodyBuilder accepted();

341

342

/** Create a 204 No Content response */

343

static HeadersBuilder<?> noContent();

344

345

/** Create a 400 Bad Request response */

346

static BodyBuilder badRequest();

347

348

/** Create a 404 Not Found response */

349

static HeadersBuilder<?> notFound();

350

351

/** Create a 422 Unprocessable Entity response */

352

static BodyBuilder unprocessableEntity();

353

354

/**

355

* Defines a builder for response headers.

356

*/

357

interface HeadersBuilder<B extends HeadersBuilder<B>> {

358

/** Add the given header value(s) under the given name */

359

B header(String headerName, String... headerValues);

360

361

/** Set the given header values under the given name */

362

B headers(HttpHeaders headers);

363

364

/** Add the given cookie */

365

B cookie(ResponseCookie cookie);

366

367

/** Set the given cookies */

368

B cookies(Consumer<MultiValueMap<String, ResponseCookie>> cookiesConsumer);

369

370

/** Allow the given origins for CORS */

371

B allow(HttpMethod... allowedMethods);

372

373

/** Set the ETag header */

374

B eTag(String etag);

375

376

/** Set the Last-Modified header */

377

B lastModified(ZonedDateTime lastModified);

378

379

/** Set the Location header */

380

B location(URI location);

381

382

/** Set the Cache-Control header */

383

B cacheControl(CacheControl cacheControl);

384

385

/** Set the Vary header */

386

B varyBy(String... requestHeaders);

387

388

/** Build the response */

389

ServerResponse build();

390

}

391

392

/**

393

* Defines a builder for responses with body.

394

*/

395

interface BodyBuilder extends HeadersBuilder<BodyBuilder> {

396

/** Set the Content-Length header */

397

BodyBuilder contentLength(long contentLength);

398

399

/** Set the Content-Type header */

400

BodyBuilder contentType(MediaType contentType);

401

402

/** Set the response body */

403

ServerResponse body(Object body);

404

405

/** Set the response body with the given type hint */

406

<T> ServerResponse body(T body, ParameterizedTypeReference<T> bodyType);

407

408

/** Render the given template with the model */

409

ServerResponse render(String name, Object... modelAttributes);

410

411

/** Render the given template with the model map */

412

ServerResponse render(String name, Map<String, ?> model);

413

}

414

415

/**

416

* Context used for writing the response.

417

*/

418

interface Context {

419

/** Return the message converters */

420

List<HttpMessageConverter<?>> messageConverters();

421

}

422

}

423

```

424

425

**Usage Examples:**

426

427

```java

428

public class ResponseExamples {

429

430

public ServerResponse jsonResponse(List<User> users) {

431

return ServerResponse.ok()

432

.contentType(MediaType.APPLICATION_JSON)

433

.body(users);

434

}

435

436

public ServerResponse createdResponse(User user) {

437

URI location = URI.create("/users/" + user.getId());

438

return ServerResponse.created(location)

439

.contentType(MediaType.APPLICATION_JSON)

440

.body(user);

441

}

442

443

public ServerResponse errorResponse(String message) {

444

Map<String, String> error = Map.of("error", message);

445

return ServerResponse.badRequest()

446

.contentType(MediaType.APPLICATION_JSON)

447

.body(error);

448

}

449

450

public ServerResponse fileResponse(Resource file) {

451

return ServerResponse.ok()

452

.contentType(MediaType.APPLICATION_OCTET_STREAM)

453

.header("Content-Disposition", "attachment; filename=\"" + file.getFilename() + "\"")

454

.body(file);

455

}

456

457

public ServerResponse templateResponse(String templateName, Map<String, Object> model) {

458

return ServerResponse.ok()

459

.render(templateName, model);

460

}

461

462

public ServerResponse redirectResponse(String url) {

463

return ServerResponse.status(HttpStatus.FOUND)

464

.location(URI.create(url))

465

.build();

466

}

467

}

468

```

469

470

### RequestPredicate

471

472

Functional interface representing a predicate (boolean-valued function) that tests server requests for routing decisions.

473

474

```java { .api }

475

/**

476

* Represents a predicate (boolean-valued function) that tests whether a request matches.

477

*/

478

@FunctionalInterface

479

public interface RequestPredicate {

480

481

/**

482

* Test this predicate against the given request.

483

* @param request the request to test against

484

* @return true if the request matches the predicate

485

*/

486

boolean test(ServerRequest request);

487

488

/** Return a composed predicate that represents logical AND */

489

default RequestPredicate and(RequestPredicate other);

490

491

/** Return a composed predicate that represents logical OR */

492

default RequestPredicate or(RequestPredicate other);

493

494

/** Return a predicate that represents logical NOT */

495

default RequestPredicate negate();

496

497

/**

498

* Optional method to return additional path variables from the predicate.

499

* Used by path pattern matching predicates.

500

*/

501

default Optional<ServerRequest> nest(ServerRequest request);

502

503

/**

504

* Accept the given visitor, calling the method that corresponds to this predicate.

505

*/

506

default void accept(Visitor visitor);

507

508

/**

509

* Visitor interface for RequestPredicate implementations.

510

*/

511

interface Visitor {

512

void method(Set<HttpMethod> methods);

513

void path(String pattern);

514

void pathExtension(String extension);

515

void header(String name, String value);

516

void queryParam(String name, String value);

517

void startNested();

518

void endNested();

519

void and();

520

void or();

521

void negate();

522

void unknown(RequestPredicate predicate);

523

}

524

}

525

```

526

527

## Utility Classes

528

529

### RouterFunctions

530

531

Central utility class providing static methods for creating and composing router functions.

532

533

```java { .api }

534

/**

535

* Central repository of utility methods that work with router functions.

536

*/

537

public abstract class RouterFunctions {

538

539

/** Create a route that matches the given predicate */

540

public static <T extends ServerResponse> RouterFunction<T> route(

541

RequestPredicate predicate, HandlerFunction<T> handlerFunction);

542

543

/** Create a nested route with the given predicate and router function */

544

public static <T extends ServerResponse> RouterFunction<T> nest(

545

RequestPredicate predicate, RouterFunction<T> routerFunction);

546

547

/** Create a router function for serving static resources */

548

public static RouterFunction<ServerResponse> resources(String pattern, Resource location);

549

550

/** Create a router function for serving resources with custom lookup */

551

public static RouterFunction<ServerResponse> resources(Function<ServerRequest, Optional<Resource>> lookupFunction);

552

553

/** Convert a RouterFunction to an HttpRequestHandler */

554

public static HttpRequestHandler toHttpHandler(RouterFunction<?> routerFunction);

555

556

/** Convert a RouterFunction to an HttpRequestHandler with MessageConverters */

557

public static HttpRequestHandler toHttpHandler(RouterFunction<?> routerFunction,

558

List<HttpMessageConverter<?>> messageConverters);

559

560

/** Convert a RouterFunction to a WebHandler */

561

public static WebHandler toWebHandler(RouterFunction<?> routerFunction);

562

}

563

```

564

565

**Usage Examples:**

566

567

```java

568

@Configuration

569

public class RouterConfiguration {

570

571

@Bean

572

public RouterFunction<ServerResponse> apiRoutes(UserHandler userHandler, ProductHandler productHandler) {

573

return nest(path("/api"),

574

nest(path("/users"),

575

route(GET(""), userHandler::findAll)

576

.andRoute(GET("/{id}"), userHandler::findById)

577

.andRoute(POST(""), userHandler::create)

578

.andRoute(PUT("/{id}"), userHandler::update)

579

.andRoute(DELETE("/{id}"), userHandler::delete)

580

).andNest(path("/products"),

581

route(GET(""), productHandler::findAll)

582

.andRoute(GET("/{id}"), productHandler::findById)

583

.andRoute(POST(""), productHandler::create)

584

)

585

);

586

}

587

588

@Bean

589

public RouterFunction<ServerResponse> staticResources() {

590

return resources("/static/**", new ClassPathResource("static/"))

591

.andRoute(resources("/uploads/**", new FileSystemResource("/var/uploads/")));

592

}

593

594

@Bean

595

public HttpRequestHandler httpHandler(RouterFunction<ServerResponse> routes) {

596

return RouterFunctions.toHttpHandler(routes);

597

}

598

}

599

```

600

601

### RequestPredicates

602

603

Utility class providing static methods for creating common request predicates used in routing.

604

605

```java { .api }

606

/**

607

* Implementations of RequestPredicate that implement various useful request matching operations.

608

*/

609

public abstract class RequestPredicates {

610

611

/** Match all requests */

612

public static RequestPredicate all();

613

614

/** Match requests with the given HTTP method */

615

public static RequestPredicate method(HttpMethod httpMethod);

616

617

/** Match requests with any of the given HTTP methods */

618

public static RequestPredicate methods(HttpMethod... httpMethods);

619

620

/** Match requests with the given path pattern */

621

public static RequestPredicate path(String pattern);

622

623

/** Match requests with the given path extension */

624

public static RequestPredicate pathExtension(String extension);

625

626

/** Match requests with the given path extension predicate */

627

public static RequestPredicate pathExtension(Predicate<String> extensionPredicate);

628

629

/** Match requests that accept the given media types */

630

public static RequestPredicate accept(MediaType... mediaTypes);

631

632

/** Match requests with the given content type */

633

public static RequestPredicate contentType(MediaType... mediaTypes);

634

635

/** Match requests with headers matching the given predicate */

636

public static RequestPredicate headers(Predicate<ServerRequest.Headers> headersPredicate);

637

638

/** Match requests with the given header name and value */

639

public static RequestPredicate header(String name, String value);

640

641

/** Match requests with query parameter matching the given predicate */

642

public static RequestPredicate queryParam(String name, Predicate<String> predicate);

643

644

/** Match requests with the given query parameter value */

645

public static RequestPredicate queryParam(String name, String value);

646

647

// HTTP method convenience methods

648

649

/** Match GET requests */

650

public static RequestPredicate GET(String pattern);

651

652

/** Match HEAD requests */

653

public static RequestPredicate HEAD(String pattern);

654

655

/** Match POST requests */

656

public static RequestPredicate POST(String pattern);

657

658

/** Match PUT requests */

659

public static RequestPredicate PUT(String pattern);

660

661

/** Match PATCH requests */

662

public static RequestPredicate PATCH(String pattern);

663

664

/** Match DELETE requests */

665

public static RequestPredicate DELETE(String pattern);

666

667

/** Match OPTIONS requests */

668

public static RequestPredicate OPTIONS(String pattern);

669

}

670

```

671

672

**Usage Examples:**

673

674

```java

675

public class PredicateExamples {

676

677

public RouterFunction<ServerResponse> complexRouting() {

678

return route(GET("/users").and(accept(MediaType.APPLICATION_JSON)), this::getUsersJson)

679

.andRoute(GET("/users").and(accept(MediaType.APPLICATION_XML)), this::getUsersXml)

680

.andRoute(POST("/users").and(contentType(MediaType.APPLICATION_JSON)), this::createUser)

681

.andRoute(path("/admin/**").and(header("Authorization", "Bearer.*")), this::adminHandler)

682

.andRoute(queryParam("version", version -> version.equals("2")), this::versionTwoHandler)

683

.andRoute(pathExtension("json"), this::jsonHandler)

684

.andRoute(pathExtension(ext -> Arrays.asList("jpg", "png", "gif").contains(ext)), this::imageHandler);

685

}

686

687

public RouterFunction<ServerResponse> conditionalRouting() {

688

RequestPredicate isApiRequest = path("/api/**");

689

RequestPredicate isAdminRequest = path("/admin/**");

690

RequestPredicate hasApiKey = header("X-API-Key", key -> !key.isEmpty());

691

RequestPredicate hasAdminRole = header("X-User-Role", "admin");

692

693

return route(isApiRequest.and(hasApiKey), this::handleApiRequest)

694

.andRoute(isAdminRequest.and(hasAdminRole), this::handleAdminRequest)

695

.andRoute(all(), this::handlePublicRequest);

696

}

697

}

698

```

699

700

## Filter Functions

701

702

### HandlerFilterFunction

703

704

Functional interface for filtering handler functions, allowing for cross-cutting concerns like authentication, logging, and error handling.

705

706

```java { .api }

707

/**

708

* Represents a filter function for handler functions that can pre- and post-process requests.

709

* @param <T> the type of response handled by the handler function being filtered

710

* @param <R> the type of response returned by the filter

711

*/

712

@FunctionalInterface

713

public interface HandlerFilterFunction<T extends ServerResponse, R extends ServerResponse> {

714

715

/**

716

* Apply this filter to the given handler function.

717

* @param request the request

718

* @param next the next handler or filter in the chain

719

* @return the filtered response

720

*/

721

R filter(ServerRequest request, HandlerFunction<T> next) throws Exception;

722

723

/** Return a composed filter that first applies this filter, then the after filter */

724

default HandlerFilterFunction<T, R> andThen(HandlerFilterFunction<R, R> after);

725

}

726

```

727

728

**Usage Examples:**

729

730

```java

731

public class FilterExamples {

732

733

public HandlerFilterFunction<ServerResponse, ServerResponse> loggingFilter() {

734

return (request, next) -> {

735

long startTime = System.currentTimeMillis();

736

log.info("Request: {} {}", request.method(), request.path());

737

738

ServerResponse response = next.handle(request);

739

740

long duration = System.currentTimeMillis() - startTime;

741

log.info("Response: {} - {}ms", response.statusCode(), duration);

742

743

return response;

744

};

745

}

746

747

public HandlerFilterFunction<ServerResponse, ServerResponse> authenticationFilter() {

748

return (request, next) -> {

749

Optional<String> authHeader = request.headers().firstHeader("Authorization");

750

751

if (authHeader.isEmpty() || !authHeader.get().startsWith("Bearer ")) {

752

return ServerResponse.status(HttpStatus.UNAUTHORIZED).build();

753

}

754

755

String token = authHeader.get().substring(7);

756

if (!tokenService.isValid(token)) {

757

return ServerResponse.status(HttpStatus.UNAUTHORIZED).build();

758

}

759

760

return next.handle(request);

761

};

762

}

763

764

public HandlerFilterFunction<ServerResponse, ServerResponse> errorHandlingFilter() {

765

return (request, next) -> {

766

try {

767

return next.handle(request);

768

} catch (ValidationException e) {

769

return ServerResponse.badRequest()

770

.contentType(MediaType.APPLICATION_JSON)

771

.body(Map.of("error", e.getMessage()));

772

} catch (NotFoundException e) {

773

return ServerResponse.notFound().build();

774

} catch (Exception e) {

775

log.error("Unexpected error processing request", e);

776

return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)

777

.body(Map.of("error", "Internal server error"));

778

}

779

};

780

}

781

782

@Bean

783

public RouterFunction<ServerResponse> filteredRoutes() {

784

return route(GET("/api/users"), userHandler::getUsers)

785

.andRoute(POST("/api/users"), userHandler::createUser)

786

.filter(loggingFilter())

787

.filter(authenticationFilter())

788

.filter(errorHandlingFilter());

789

}

790

}

791

```