Confidential │ ©2020 VMware, Inc.
Introduction to
WebMvc.fn
Functional Servlet Endpoints
Arjen Poutsma (@poutsma)
Sr. Staff Engineer at VMware
3rd September 2020
Confidential │ ©2020 VMware, Inc.
Agenda
2
Handler Functions
Router Functions
Filter Functions
Final Comparison
Confidential │ ©2020 VMware, Inc. 3
WebMvc.fn was introduced
in Spring 5.2 and is fully
compatible with Spring Boot
Confidential │ ©2020 VMware, Inc.
Handler Functions
Map from ServerRequest to ServerResponse
4
Confidential │ ©2020 VMware, Inc. 5
Map from ServerRequest to ServerResponse
Handler Functions
@Controller
class PersonController {
PersonRepository repository;
ResponseEntity<Person> getPerson(@PathVariable String id) {
Person person = repository.findById(id);
return ResponseEntity.ok()
.contentType(APPLICATION_JSON)
.body(person);
}
ResponseEntity<List<Person>> getPeople() {
List<Person> people = repository.findAll();
return ResponseEntity.ok()
.contentType(APPLICATION_JSON)
.body(people);
}
}
Web MVC
Confidential │ ©2020 VMware, Inc. 5
Map from ServerRequest to ServerResponse
Handler Functions
@Controller
class PersonHandler {
PersonRepository repository;
ServerResponse getPerson(ServerRequest request) {
String id = request.pathVariable("id");
Person person = repository.findById(id);
return ServerResponse.ok()
.contentType(APPLICATION_JSON)
.body(person);
}
ServerResponse getPeople(ServerRequest request) {
List<Person> people = repository.findAll();
return ServerResponse.ok()
.contentType(APPLICATION_JSON)
.body(people);
}
}
WebMvc.fn
Confidential │ ©2020 VMware, Inc.
Web MVC WebMvc.fn
6
Handler Function Comparison
@Controller
class PersonController {
PersonRepository repository;
ResponseEntity<Person> getPerson(@PathVariable String id) {
Person person = repository.findById(id);
return ResponseEntity.ok()
.contentType(APPLICATION_JSON)
.body(person);
}
ResponseEntity<List<Person>> getPeople() {
List<Person> people = repository.findAll();
return ResponseEntity.ok()
.contentType(APPLICATION_JSON)
.body(people);
}
}
@Controller
class PersonHandler {
PersonRepository repository;
ServerResponse getPerson(ServerRequest request) {
String id = request.pathVariable("id");
Person person = repository.findById(id);
return ServerResponse.ok()
.contentType(APPLICATION_JSON)
.body(person);
}
ServerResponse getPeople(ServerRequest request) {
List<Person> people = repository.findAll();
return ServerResponse.ok()
.contentType(APPLICATION_JSON)
.body(people);
}
}
Confidential │ ©2020 VMware, Inc. 7
Handler Functions map from
ServerRequest to ServerResponse
• both are immutable
• create response with builder
Confidential │ ©2020 VMware, Inc.
Router Functions
Map from ServerRequest to HandlerFunction
8
Confidential │ ©2020 VMware, Inc. 9
Map from ServerRequest to HandlerFunction
Router Functions
@Controller
@RequestMapping("/people")
class PersonController {
PersonRepository repository;
@RequestMapping(value = "/{id}", method = GET)
ResponseEntity<Person> getPerson(@PathVariable String id) {
...
}
@RequestMapping(method = GET)
ResponseEntity<List<Person>> getPeople() {
...
}
}
Web MVC
Confidential │ ©2020 VMware, Inc. 9
Map from ServerRequest to HandlerFunction
Router Functions
@Controller
class PersonHandler {
PersonRepository repository;
ServerResponse getPerson(ServerRequest request) {
...
}
ServerResponse getPeople(ServerRequest request) {
...
}
@Bean
static RouterFunction router(PersonHandler handler) {
return route()
.GET("/people/{id}", handler::getPerson)
.GET(“/people", handler::getPeople)
.build();
}
}
WebMvc.fn
Confidential │ ©2020 VMware, Inc. 9
Map from ServerRequest to HandlerFunction
Router Functions
@Controller
class PersonHandler {
PersonRepository repository;
ServerResponse getPerson(ServerRequest request) {
...
}
ServerResponse getPeople(ServerRequest request) {
...
}
@Bean
static RouterFunction router(PersonHandler handler) {
return route()
.path(“/people", builder -> builder
.GET("/{id}", handler::getPerson)
.GET("/", handler::getPeople));
.build();
}
}
WebMvc.fn
Confidential │ ©2020 VMware, Inc.
Web MVC WebMvc.fn
10
Router Function Comparison
@Controller
class PersonHandler {
PersonRepository repository;
ServerResponse getPerson(ServerRequest request) {
...
}
ServerResponse getPeople(ServerRequest request) {
...
}
@Bean
static RouterFunction router(PersonHandler handler) {
return route()
.path(“/people", builder -> builder
.GET("/{id}", handler::getPerson)
.GET("/", handler::getPeople));
.build();
}
}
@Controller
@RequestMapping(“/people")
class PersonController {
PersonRepository repository;
@RequestMapping(value = "/{id}", method = GET)
ResponseEntity<Person> getPerson(@PathVariable String id) {
...
}
@RequestMapping(method = GET)
ResponseEntity<List<Person>> getPeople() {
...
}
}
Confidential │ ©2020 VMware, Inc.
Web MVC WebMvc.fn
10
Router Function Comparison
@Controller
class PersonHandler {
PersonRepository repository;
ServerResponse getPerson(ServerRequest request) {
...
}
ServerResponse getPeople(ServerRequest request) {
...
}
@Bean
static RouterFunction router(PersonHandler handler) {
return route()
.path(“/people", builder -> builder
.GET("/{id}", handler::getPerson)
.GET("/", handler::getPeople));
.build();
}
}
@Controller
@RequestMapping(“/people")
class PersonController {
PersonRepository repository;
@RequestMapping(value = "/{id}", method = GET)
ResponseEntity<Person> getPerson(@PathVariable String id) {
...
}
@RequestMapping(method = GET)
ResponseEntity<List<Person>> getPeople() {
...
}
}
Confidential │ ©2020 VMware, Inc. 11
Router Functions created using builder
• GET, POST, PUT, etc.
• resources
• nesting
• more powerful abstraction underneath:
RequestPredicate
Confidential │ ©2020 VMware, Inc.
Request Predicates
Evaluate on a ServerRequest
12
Confidential │ ©2020 VMware, Inc. 13
Evaluate on a ServerRequest
Request Predicates
RequestPredicate get = RequestPredicates.method(HttpMethod.GET);
RequestPredicate people = RequestPredicates.path("/people");
RequestPredicate getPeople = get.and(people); // or RequestPredicates.GET("/people")
RequestPredicate acceptJson = RequestPredicates.accept(APPLICATION_JSON);
RequestPredicate extensionJson = RequestPredicates.pathExtension("json");
RequestPredicate paramTypeJson = RequestPredicates.param("type", "json");
RequestPredicate json = acceptJson.or(extensionJson).or(paramTypeJson);
Confidential │ ©2020 VMware, Inc. 14
Evaluate on a ServerRequest
Request Predicates
@Controller
class PersonHandler {
PersonRepository repository;
...
ServerResponse createPerson(ServerRequest request) {
Person person = request.body(Person.class);
repository.savePerson(person);
return ServerResponse.ok()
.build();
}
@Bean
static RouterFunction router(PersonHandler handler) {
return route()
.path("/people", b -> b
.GET("/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET("/", accept(APPLICATION_JSON), handler::getPeople)
.POST("/", contentType(APPLICATION_JSON), handler::createPerson))
.build();
}
}
Confidential │ ©2020 VMware, Inc. 14
Evaluate on a ServerRequest
Request Predicates
@Controller
class PersonHandler {
PersonRepository repository;
...
ServerResponse createPerson(ServerRequest request) {
Person person = request.body(Person.class);
this.repository.savePerson(person);
return ServerResponse.ok()
.build();
}
@Bean
static RouterFunction<ServerResponse> routerFunction(PersonHandler handler) {
return route()
.path(“/people", b -> b
.nest(accept(APPLICATION_JSON), b1 -> b1
.GET("/{id}", handler::getPerson)
.GET("/", handler::getPeople)
)
.POST("/", contentType(APPLICATION_JSON), handler::createPerson))
.build();
}
}
Confidential │ ©2020 VMware, Inc.
Web MVC WebMvc.fn
15
Request Predicates Comparison
@Controller
@RequestMapping(“/people”)
class PersonController {
PersonRepository repository;
@RequestMapping(value = "/{id}",
method = GET,
produces = APPLICATION_JSON_VALUE)
ResponseEntity<Person> getPerson(@PathVariable String id) {
...
}
@RequestMapping(method = GET,
produces = APPLICATION_JSON_VALUE)
ResponseEntity<List<Person>> showPeople() {
...
}
@RequestMapping(method = POST,
consumes = APPLICATION_JSON_VALUE)
void createPerson(RequestEntity<Person> person) {
...
}
}
@Controller
class PersonHandler {
PersonRepository repository;
...
@Bean
static RouterFunction router(PersonHandler handler) {
return route()
.path(“/people", b -> b
.GET("/{id}", accept(APPLICATION_JSON), handler::getP
.GET("/", accept(APPLICATION_JSON), handler::getPeopl
.POST("/", contentType(APPLICATION_JSON), handler::cr
.build();
}
}
Confidential │ ©2020 VMware, Inc.
Web MVC WebMvc.fn
15
Request Predicates Comparison
@Controller
@RequestMapping(“/people”)
class PersonController {
PersonRepository repository;
@RequestMapping(value = "/{id}",
method = GET,
produces = APPLICATION_JSON_VALUE)
ResponseEntity<Person> getPerson(@PathVariable String id) {
...
}
@RequestMapping(method = GET,
produces = APPLICATION_JSON_VALUE)
ResponseEntity<List<Person>> showPeople() {
...
}
@RequestMapping(method = POST,
consumes = APPLICATION_JSON_VALUE)
void createPerson(RequestEntity<Person> person) {
...
}
}
@Controller
class PersonHandler {
PersonRepository repository;
...
@Bean
static RouterFunction router(PersonHandler handler) {
return route()
.path(“/people", b -> b
.GET("/{id}", accept(APPLICATION_JSON), handler::getP
.GET("/", accept(APPLICATION_JSON), handler::getPeopl
.POST("/", contentType(APPLICATION_JSON), handler::cr
.build();
}
}
Confidential │ ©2020 VMware, Inc.
Web MVC WebMvc.fn
16
Request Predicates Comparison
@Controller
class PersonHandler {
PersonRepository repository;
...
@Bean
static RouterFunction<ServerResponse> routerFunction(Person
return route()
.path("/person", b -> b
.nest(accept(APPLICATION_JSON), b1 -> b1
.GET("/{id}", handler::getPerson)
.GET("/", handler::getPeople)
)
.POST("/", contentType(APPLICATION_JSON), handler::cr
.build();
}
}
?
Confidential │ ©2020 VMware, Inc.
Handler Filter Functions
Maps ServerRequest and HandlerFunction
to ServerResponse
17
Confidential │ ©2020 VMware, Inc. 18
Maps ServerRequest and HandlerFunction to ServerResponse
Handler Filter Functions
@Controller
@RequestMapping("/people")
class PersonController {
PersonRepository repository;
...
@ExceptionHandler(NotFoundException.class)
ResponseEntity notFound() {
return ResponseEntity.notFound().build()
}
}
Web MVC
Confidential │ ©2020 VMware, Inc. 18
Maps ServerRequest and HandlerFunction to ServerResponse
Handler Filter Functions
@Controller
class PersonHandler {
PersonRepository repository;
...
ServerResponse notFound(Throwable throwable, ServerRequest request) {
return ServerResponse.notFound().build();
}
@Bean
static RouterFunction<ServerResponse> routerFunction(PersonHandler handler) {
return route()
.path(“/people", b -> b
.nest(accept(APPLICATION_JSON), b1 -> b1
.GET("/{id}", handler::getPerson)
.GET("/", handler::getPeople)
)
.POST("/", contentType(APPLICATION_JSON), handler::createPerson))
.onError(NotFoundException.class, handler::notFound)
.build();
}
}
WebMvc.fn
Confidential │ ©2020 VMware, Inc. 18
Maps ServerRequest and HandlerFunction to ServerResponse
Handler Filter Functions
@Controller
class PersonHandler {
PersonRepository repository;
...
ServerResponse notFound(Throwable throwable, ServerRequest request) {
return ServerResponse.notFound().build();
}
@Bean
static RouterFunction<ServerResponse> routerFunction(PersonHandler handler) {
return route()
.path(“/people", b -> b
.nest(accept(APPLICATION_JSON), b1 -> b1
.GET("/{id}", handler::getPerson)
.GET("/", handler::getPeople)
.onError(NotFoundException.class, handler::notFound)
)
.POST("/", contentType(APPLICATION_JSON), handler::createPerson))
.build();
}
}
WebMvc.fn
Confidential │ ©2020 VMware, Inc.
Web MVC WebMvc.fn
19
Handler Filter Function Comparison
@Controller
@RequestMapping("/people")
class PersonController {
PersonRepository repository;
...
@ExceptionHandler(NotFoundException.class)
ResponseEntity notFound() {
return ResponseEntity.notFound().build()
}
}
@Controller
class PersonHandler {
PersonRepository repository;
...
ServerResponse notFound(Throwable throwable, ServerRequest request) {
return ServerResponse.notFound().build();
}
@Bean
static RouterFunction<ServerResponse> routerFunction(PersonHandler handler) {
return route()
.path("/person", b -> b
.nest(accept(APPLICATION_JSON), b1 -> b1
.GET("/{id}", handler::getPerson)
.GET("/", handler::getPeople)
)
.POST("/", contentType(APPLICATION_JSON), handler::createPerson))
.onError(NotFoundException.class, handler::notFound)
.build();
}
}
Confidential │ ©2020 VMware, Inc.
Web MVC WebMvc.fn
19
Handler Filter Function Comparison
@Controller
@RequestMapping("/people")
class PersonController {
PersonRepository repository;
...
@ExceptionHandler(NotFoundException.class)
ResponseEntity notFound() {
return ResponseEntity.notFound().build()
}
}
@Controller
class PersonHandler {
PersonRepository repository;
...
ServerResponse notFound(Throwable throwable, ServerRequest request) {
return ServerResponse.notFound().build();
}
@Bean
static RouterFunction<ServerResponse> routerFunction(PersonHandler handler) {
return route()
.path("/person", b -> b
.nest(accept(APPLICATION_JSON), b1 -> b1
.GET("/{id}", handler::getPerson)
.GET("/", handler::getPeople)
)
.POST("/", contentType(APPLICATION_JSON), handler::createPerson))
.onError(NotFoundException.class, handler::notFound)
.build();
}
}
Confidential │ ©2020 VMware, Inc.
Final Comparison
20
Confidential │ ©2020 VMware, Inc.
• Functions
• Code
• Rigid handler methods
(ServerRequest→ServerResponse)
• Flexible routing
• Annotations
• Meta-data
• Flexible handler methods
(parameters & return type)
• Limited routing
Web MVC WebMvc.fn
21
Request Predicates Comparison
Confidential │ ©2020 VMware, Inc.
Thank You

Introduction to WebMvc.fn

  • 1.
    Confidential │ ©2020VMware, Inc. Introduction to WebMvc.fn Functional Servlet Endpoints Arjen Poutsma (@poutsma) Sr. Staff Engineer at VMware 3rd September 2020
  • 2.
    Confidential │ ©2020VMware, Inc. Agenda 2 Handler Functions Router Functions Filter Functions Final Comparison
  • 3.
    Confidential │ ©2020VMware, Inc. 3 WebMvc.fn was introduced in Spring 5.2 and is fully compatible with Spring Boot
  • 4.
    Confidential │ ©2020VMware, Inc. Handler Functions Map from ServerRequest to ServerResponse 4
  • 5.
    Confidential │ ©2020VMware, Inc. 5 Map from ServerRequest to ServerResponse Handler Functions @Controller class PersonController { PersonRepository repository; ResponseEntity<Person> getPerson(@PathVariable String id) { Person person = repository.findById(id); return ResponseEntity.ok() .contentType(APPLICATION_JSON) .body(person); } ResponseEntity<List<Person>> getPeople() { List<Person> people = repository.findAll(); return ResponseEntity.ok() .contentType(APPLICATION_JSON) .body(people); } } Web MVC
  • 6.
    Confidential │ ©2020VMware, Inc. 5 Map from ServerRequest to ServerResponse Handler Functions @Controller class PersonHandler { PersonRepository repository; ServerResponse getPerson(ServerRequest request) { String id = request.pathVariable("id"); Person person = repository.findById(id); return ServerResponse.ok() .contentType(APPLICATION_JSON) .body(person); } ServerResponse getPeople(ServerRequest request) { List<Person> people = repository.findAll(); return ServerResponse.ok() .contentType(APPLICATION_JSON) .body(people); } } WebMvc.fn
  • 7.
    Confidential │ ©2020VMware, Inc. Web MVC WebMvc.fn 6 Handler Function Comparison @Controller class PersonController { PersonRepository repository; ResponseEntity<Person> getPerson(@PathVariable String id) { Person person = repository.findById(id); return ResponseEntity.ok() .contentType(APPLICATION_JSON) .body(person); } ResponseEntity<List<Person>> getPeople() { List<Person> people = repository.findAll(); return ResponseEntity.ok() .contentType(APPLICATION_JSON) .body(people); } } @Controller class PersonHandler { PersonRepository repository; ServerResponse getPerson(ServerRequest request) { String id = request.pathVariable("id"); Person person = repository.findById(id); return ServerResponse.ok() .contentType(APPLICATION_JSON) .body(person); } ServerResponse getPeople(ServerRequest request) { List<Person> people = repository.findAll(); return ServerResponse.ok() .contentType(APPLICATION_JSON) .body(people); } }
  • 8.
    Confidential │ ©2020VMware, Inc. 7 Handler Functions map from ServerRequest to ServerResponse • both are immutable • create response with builder
  • 9.
    Confidential │ ©2020VMware, Inc. Router Functions Map from ServerRequest to HandlerFunction 8
  • 10.
    Confidential │ ©2020VMware, Inc. 9 Map from ServerRequest to HandlerFunction Router Functions @Controller @RequestMapping("/people") class PersonController { PersonRepository repository; @RequestMapping(value = "/{id}", method = GET) ResponseEntity<Person> getPerson(@PathVariable String id) { ... } @RequestMapping(method = GET) ResponseEntity<List<Person>> getPeople() { ... } } Web MVC
  • 11.
    Confidential │ ©2020VMware, Inc. 9 Map from ServerRequest to HandlerFunction Router Functions @Controller class PersonHandler { PersonRepository repository; ServerResponse getPerson(ServerRequest request) { ... } ServerResponse getPeople(ServerRequest request) { ... } @Bean static RouterFunction router(PersonHandler handler) { return route() .GET("/people/{id}", handler::getPerson) .GET(“/people", handler::getPeople) .build(); } } WebMvc.fn
  • 12.
    Confidential │ ©2020VMware, Inc. 9 Map from ServerRequest to HandlerFunction Router Functions @Controller class PersonHandler { PersonRepository repository; ServerResponse getPerson(ServerRequest request) { ... } ServerResponse getPeople(ServerRequest request) { ... } @Bean static RouterFunction router(PersonHandler handler) { return route() .path(“/people", builder -> builder .GET("/{id}", handler::getPerson) .GET("/", handler::getPeople)); .build(); } } WebMvc.fn
  • 13.
    Confidential │ ©2020VMware, Inc. Web MVC WebMvc.fn 10 Router Function Comparison @Controller class PersonHandler { PersonRepository repository; ServerResponse getPerson(ServerRequest request) { ... } ServerResponse getPeople(ServerRequest request) { ... } @Bean static RouterFunction router(PersonHandler handler) { return route() .path(“/people", builder -> builder .GET("/{id}", handler::getPerson) .GET("/", handler::getPeople)); .build(); } } @Controller @RequestMapping(“/people") class PersonController { PersonRepository repository; @RequestMapping(value = "/{id}", method = GET) ResponseEntity<Person> getPerson(@PathVariable String id) { ... } @RequestMapping(method = GET) ResponseEntity<List<Person>> getPeople() { ... } }
  • 14.
    Confidential │ ©2020VMware, Inc. Web MVC WebMvc.fn 10 Router Function Comparison @Controller class PersonHandler { PersonRepository repository; ServerResponse getPerson(ServerRequest request) { ... } ServerResponse getPeople(ServerRequest request) { ... } @Bean static RouterFunction router(PersonHandler handler) { return route() .path(“/people", builder -> builder .GET("/{id}", handler::getPerson) .GET("/", handler::getPeople)); .build(); } } @Controller @RequestMapping(“/people") class PersonController { PersonRepository repository; @RequestMapping(value = "/{id}", method = GET) ResponseEntity<Person> getPerson(@PathVariable String id) { ... } @RequestMapping(method = GET) ResponseEntity<List<Person>> getPeople() { ... } }
  • 15.
    Confidential │ ©2020VMware, Inc. 11 Router Functions created using builder • GET, POST, PUT, etc. • resources • nesting • more powerful abstraction underneath: RequestPredicate
  • 16.
    Confidential │ ©2020VMware, Inc. Request Predicates Evaluate on a ServerRequest 12
  • 17.
    Confidential │ ©2020VMware, Inc. 13 Evaluate on a ServerRequest Request Predicates RequestPredicate get = RequestPredicates.method(HttpMethod.GET); RequestPredicate people = RequestPredicates.path("/people"); RequestPredicate getPeople = get.and(people); // or RequestPredicates.GET("/people") RequestPredicate acceptJson = RequestPredicates.accept(APPLICATION_JSON); RequestPredicate extensionJson = RequestPredicates.pathExtension("json"); RequestPredicate paramTypeJson = RequestPredicates.param("type", "json"); RequestPredicate json = acceptJson.or(extensionJson).or(paramTypeJson);
  • 18.
    Confidential │ ©2020VMware, Inc. 14 Evaluate on a ServerRequest Request Predicates @Controller class PersonHandler { PersonRepository repository; ... ServerResponse createPerson(ServerRequest request) { Person person = request.body(Person.class); repository.savePerson(person); return ServerResponse.ok() .build(); } @Bean static RouterFunction router(PersonHandler handler) { return route() .path("/people", b -> b .GET("/{id}", accept(APPLICATION_JSON), handler::getPerson) .GET("/", accept(APPLICATION_JSON), handler::getPeople) .POST("/", contentType(APPLICATION_JSON), handler::createPerson)) .build(); } }
  • 19.
    Confidential │ ©2020VMware, Inc. 14 Evaluate on a ServerRequest Request Predicates @Controller class PersonHandler { PersonRepository repository; ... ServerResponse createPerson(ServerRequest request) { Person person = request.body(Person.class); this.repository.savePerson(person); return ServerResponse.ok() .build(); } @Bean static RouterFunction<ServerResponse> routerFunction(PersonHandler handler) { return route() .path(“/people", b -> b .nest(accept(APPLICATION_JSON), b1 -> b1 .GET("/{id}", handler::getPerson) .GET("/", handler::getPeople) ) .POST("/", contentType(APPLICATION_JSON), handler::createPerson)) .build(); } }
  • 20.
    Confidential │ ©2020VMware, Inc. Web MVC WebMvc.fn 15 Request Predicates Comparison @Controller @RequestMapping(“/people”) class PersonController { PersonRepository repository; @RequestMapping(value = "/{id}", method = GET, produces = APPLICATION_JSON_VALUE) ResponseEntity<Person> getPerson(@PathVariable String id) { ... } @RequestMapping(method = GET, produces = APPLICATION_JSON_VALUE) ResponseEntity<List<Person>> showPeople() { ... } @RequestMapping(method = POST, consumes = APPLICATION_JSON_VALUE) void createPerson(RequestEntity<Person> person) { ... } } @Controller class PersonHandler { PersonRepository repository; ... @Bean static RouterFunction router(PersonHandler handler) { return route() .path(“/people", b -> b .GET("/{id}", accept(APPLICATION_JSON), handler::getP .GET("/", accept(APPLICATION_JSON), handler::getPeopl .POST("/", contentType(APPLICATION_JSON), handler::cr .build(); } }
  • 21.
    Confidential │ ©2020VMware, Inc. Web MVC WebMvc.fn 15 Request Predicates Comparison @Controller @RequestMapping(“/people”) class PersonController { PersonRepository repository; @RequestMapping(value = "/{id}", method = GET, produces = APPLICATION_JSON_VALUE) ResponseEntity<Person> getPerson(@PathVariable String id) { ... } @RequestMapping(method = GET, produces = APPLICATION_JSON_VALUE) ResponseEntity<List<Person>> showPeople() { ... } @RequestMapping(method = POST, consumes = APPLICATION_JSON_VALUE) void createPerson(RequestEntity<Person> person) { ... } } @Controller class PersonHandler { PersonRepository repository; ... @Bean static RouterFunction router(PersonHandler handler) { return route() .path(“/people", b -> b .GET("/{id}", accept(APPLICATION_JSON), handler::getP .GET("/", accept(APPLICATION_JSON), handler::getPeopl .POST("/", contentType(APPLICATION_JSON), handler::cr .build(); } }
  • 22.
    Confidential │ ©2020VMware, Inc. Web MVC WebMvc.fn 16 Request Predicates Comparison @Controller class PersonHandler { PersonRepository repository; ... @Bean static RouterFunction<ServerResponse> routerFunction(Person return route() .path("/person", b -> b .nest(accept(APPLICATION_JSON), b1 -> b1 .GET("/{id}", handler::getPerson) .GET("/", handler::getPeople) ) .POST("/", contentType(APPLICATION_JSON), handler::cr .build(); } } ?
  • 23.
    Confidential │ ©2020VMware, Inc. Handler Filter Functions Maps ServerRequest and HandlerFunction to ServerResponse 17
  • 24.
    Confidential │ ©2020VMware, Inc. 18 Maps ServerRequest and HandlerFunction to ServerResponse Handler Filter Functions @Controller @RequestMapping("/people") class PersonController { PersonRepository repository; ... @ExceptionHandler(NotFoundException.class) ResponseEntity notFound() { return ResponseEntity.notFound().build() } } Web MVC
  • 25.
    Confidential │ ©2020VMware, Inc. 18 Maps ServerRequest and HandlerFunction to ServerResponse Handler Filter Functions @Controller class PersonHandler { PersonRepository repository; ... ServerResponse notFound(Throwable throwable, ServerRequest request) { return ServerResponse.notFound().build(); } @Bean static RouterFunction<ServerResponse> routerFunction(PersonHandler handler) { return route() .path(“/people", b -> b .nest(accept(APPLICATION_JSON), b1 -> b1 .GET("/{id}", handler::getPerson) .GET("/", handler::getPeople) ) .POST("/", contentType(APPLICATION_JSON), handler::createPerson)) .onError(NotFoundException.class, handler::notFound) .build(); } } WebMvc.fn
  • 26.
    Confidential │ ©2020VMware, Inc. 18 Maps ServerRequest and HandlerFunction to ServerResponse Handler Filter Functions @Controller class PersonHandler { PersonRepository repository; ... ServerResponse notFound(Throwable throwable, ServerRequest request) { return ServerResponse.notFound().build(); } @Bean static RouterFunction<ServerResponse> routerFunction(PersonHandler handler) { return route() .path(“/people", b -> b .nest(accept(APPLICATION_JSON), b1 -> b1 .GET("/{id}", handler::getPerson) .GET("/", handler::getPeople) .onError(NotFoundException.class, handler::notFound) ) .POST("/", contentType(APPLICATION_JSON), handler::createPerson)) .build(); } } WebMvc.fn
  • 27.
    Confidential │ ©2020VMware, Inc. Web MVC WebMvc.fn 19 Handler Filter Function Comparison @Controller @RequestMapping("/people") class PersonController { PersonRepository repository; ... @ExceptionHandler(NotFoundException.class) ResponseEntity notFound() { return ResponseEntity.notFound().build() } } @Controller class PersonHandler { PersonRepository repository; ... ServerResponse notFound(Throwable throwable, ServerRequest request) { return ServerResponse.notFound().build(); } @Bean static RouterFunction<ServerResponse> routerFunction(PersonHandler handler) { return route() .path("/person", b -> b .nest(accept(APPLICATION_JSON), b1 -> b1 .GET("/{id}", handler::getPerson) .GET("/", handler::getPeople) ) .POST("/", contentType(APPLICATION_JSON), handler::createPerson)) .onError(NotFoundException.class, handler::notFound) .build(); } }
  • 28.
    Confidential │ ©2020VMware, Inc. Web MVC WebMvc.fn 19 Handler Filter Function Comparison @Controller @RequestMapping("/people") class PersonController { PersonRepository repository; ... @ExceptionHandler(NotFoundException.class) ResponseEntity notFound() { return ResponseEntity.notFound().build() } } @Controller class PersonHandler { PersonRepository repository; ... ServerResponse notFound(Throwable throwable, ServerRequest request) { return ServerResponse.notFound().build(); } @Bean static RouterFunction<ServerResponse> routerFunction(PersonHandler handler) { return route() .path("/person", b -> b .nest(accept(APPLICATION_JSON), b1 -> b1 .GET("/{id}", handler::getPerson) .GET("/", handler::getPeople) ) .POST("/", contentType(APPLICATION_JSON), handler::createPerson)) .onError(NotFoundException.class, handler::notFound) .build(); } }
  • 29.
    Confidential │ ©2020VMware, Inc. Final Comparison 20
  • 30.
    Confidential │ ©2020VMware, Inc. • Functions • Code • Rigid handler methods (ServerRequest→ServerResponse) • Flexible routing • Annotations • Meta-data • Flexible handler methods (parameters & return type) • Limited routing Web MVC WebMvc.fn 21 Request Predicates Comparison
  • 31.
    Confidential │ ©2020VMware, Inc. Thank You