mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Extract some WebFlux functional snippets
See gh-36089
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlerclasses;
|
||||
|
||||
public record Person(String name) { }
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlerclasses;
|
||||
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
|
||||
|
||||
// tag::snippet[]
|
||||
public class PersonHandler {
|
||||
|
||||
private final PersonRepository repository;
|
||||
|
||||
public PersonHandler(PersonRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
// listPeople is a handler function that returns all Person objects found
|
||||
// in the repository as JSON
|
||||
public Mono<ServerResponse> listPeople(ServerRequest request) {
|
||||
Flux<Person> people = repository.allPeople();
|
||||
return ok().contentType(APPLICATION_JSON).body(people, Person.class);
|
||||
}
|
||||
|
||||
// createPerson is a handler function that stores a new Person contained
|
||||
// in the request body.
|
||||
// Note that PersonRepository.savePerson(Person) returns Mono<Void>: an empty
|
||||
// Mono that emits a completion signal when the person has been read from the
|
||||
// request and stored. So we use the build(Publisher<Void>) method to send a
|
||||
// response when that completion signal is received (that is, when the Person
|
||||
// has been saved)
|
||||
public Mono<ServerResponse> createPerson(ServerRequest request) {
|
||||
Mono<Person> person = request.bodyToMono(Person.class);
|
||||
return ok().build(repository.savePerson(person));
|
||||
}
|
||||
|
||||
// getPerson is a handler function that returns a single person, identified by
|
||||
// the id path variable. We retrieve that Person from the repository and create
|
||||
// a JSON response, if it is found. If it is not found, we use switchIfEmpty(Mono<T>)
|
||||
// to return a 404 Not Found response.
|
||||
public Mono<ServerResponse> getPerson(ServerRequest request) {
|
||||
int personId = Integer.valueOf(request.pathVariable("id"));
|
||||
return repository.getPerson(personId)
|
||||
.flatMap(person -> ok().contentType(APPLICATION_JSON).bodyValue(person))
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlerclasses;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface PersonRepository {
|
||||
|
||||
Flux<Person> allPeople();
|
||||
|
||||
Mono<Void> savePerson(Mono<Person> person);
|
||||
|
||||
Mono<Person> getPerson(int id);
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlerfilterfunction;
|
||||
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.PersonHandler;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
|
||||
|
||||
public class RouterConfiguration {
|
||||
|
||||
public RouterFunction<ServerResponse> route(PersonHandler handler) {
|
||||
// tag::snippet[]
|
||||
SecurityManager securityManager = getSecurityManager();
|
||||
|
||||
RouterFunction<ServerResponse> route = RouterFunctions.route()
|
||||
.path("/person", b1 -> b1
|
||||
.nest(accept(APPLICATION_JSON), b2 -> b2
|
||||
.GET("/{id}", handler::getPerson)
|
||||
.GET(handler::listPeople))
|
||||
.POST(handler::createPerson))
|
||||
.filter((request, next) -> {
|
||||
if (securityManager.allowAccessTo(request.path())) {
|
||||
return next.handle(request);
|
||||
}
|
||||
else {
|
||||
return ServerResponse.status(UNAUTHORIZED).build();
|
||||
}
|
||||
}).build();
|
||||
// end::snippet[]
|
||||
return route;
|
||||
}
|
||||
|
||||
SecurityManager getSecurityManager() {
|
||||
return path -> false;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlerfilterfunction;
|
||||
|
||||
public interface SecurityManager {
|
||||
|
||||
boolean allowAccessTo(String path);
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlervalidation;
|
||||
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.Person;
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.PersonRepository;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.ServerWebInputException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
|
||||
|
||||
// tag::snippet[]
|
||||
public class PersonHandler {
|
||||
|
||||
// Create Validator instance
|
||||
private final Validator validator = new PersonValidator();
|
||||
|
||||
private final PersonRepository repository;
|
||||
|
||||
public PersonHandler(PersonRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public Mono<ServerResponse> createPerson(ServerRequest request) {
|
||||
// Apply validation
|
||||
Mono<Person> person = request.bodyToMono(Person.class).doOnNext(this::validate);
|
||||
return ok().build(repository.savePerson(person));
|
||||
}
|
||||
|
||||
private void validate(Person person) {
|
||||
Errors errors = new BeanPropertyBindingResult(person, "person");
|
||||
validator.validate(person, errors);
|
||||
if (errors.hasErrors()) {
|
||||
// Raise exception for a 400 response
|
||||
throw new ServerWebInputException(errors.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlervalidation;
|
||||
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.Person;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
public class PersonValidator implements Validator {
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return Person.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Object target, Errors errors) {
|
||||
// Validation logic
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnpredicates;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
|
||||
|
||||
public class RouterConfiguration {
|
||||
|
||||
public RouterFunction<ServerResponse> route() {
|
||||
// tag::snippet[]
|
||||
RouterFunction<ServerResponse> route = RouterFunctions.route()
|
||||
.GET("/hello-world", accept(MediaType.TEXT_PLAIN),
|
||||
request -> ServerResponse.ok().bodyValue("Hello World")).build();
|
||||
// end::snippet[]
|
||||
return route;
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnrequest;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.codec.multipart.FilePartEvent;
|
||||
import org.springframework.http.codec.multipart.FormPartEvent;
|
||||
import org.springframework.http.codec.multipart.PartEvent;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public class PartEventHandler {
|
||||
|
||||
public void handle(ServerRequest request) {
|
||||
// tag::snippet[]
|
||||
request.bodyToFlux(PartEvent.class).windowUntil(PartEvent::isLast)
|
||||
.concatMap(p -> p.switchOnFirst((signal, partEvents) -> {
|
||||
if (signal.hasValue()) {
|
||||
PartEvent event = signal.get();
|
||||
if (event instanceof FormPartEvent formEvent) {
|
||||
String value = formEvent.value();
|
||||
// handle form field
|
||||
}
|
||||
else if (event instanceof FilePartEvent fileEvent) {
|
||||
String filename = fileEvent.filename();
|
||||
Flux<DataBuffer> contents = partEvents.map(PartEvent::content);
|
||||
// handle file upload
|
||||
}
|
||||
else {
|
||||
return Mono.error(new RuntimeException("Unexpected event: " + event));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return partEvents; // either complete or error signal
|
||||
}
|
||||
return Mono.empty();
|
||||
}));
|
||||
// end::snippet[]
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnrequest;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
|
||||
public class RequestHandler {
|
||||
|
||||
public void bind(ServerRequest request) {
|
||||
// tag::snippet[]
|
||||
Mono<Pet> pet = request.bind(Pet.class, dataBinder -> dataBinder.setAllowedFields("name"));
|
||||
// end::snippet[]
|
||||
}
|
||||
|
||||
record Pet(String name) { }
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnresponse;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public class ResponseHandler {
|
||||
|
||||
public Mono<ServerResponse> createResponse() {
|
||||
// tag::snippet[]
|
||||
Mono<Person> person = getPerson();
|
||||
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class);
|
||||
// end::snippet[]
|
||||
}
|
||||
|
||||
private Mono<Person> getPerson() {
|
||||
return Mono.just(new Person("foo"));
|
||||
}
|
||||
|
||||
record Person(String name) { }
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnroutes;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.Person;
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.PersonHandler;
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.PersonRepository;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
|
||||
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
||||
|
||||
public class RouterConfiguration {
|
||||
|
||||
public RouterFunction<ServerResponse> routes() {
|
||||
// tag::snippet[]
|
||||
PersonRepository repository = getPersonRepository();
|
||||
PersonHandler handler = new PersonHandler(repository);
|
||||
|
||||
RouterFunction<ServerResponse> otherRoute = getOtherRoute();
|
||||
|
||||
RouterFunction<ServerResponse> route = route()
|
||||
// GET /person/{id} with an Accept header that matches JSON is routed to PersonHandler.getPerson
|
||||
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
|
||||
// GET /person with an Accept header that matches JSON is routed to PersonHandler.listPeople
|
||||
.GET("/person", accept(APPLICATION_JSON), handler::listPeople)
|
||||
// POST /person with no additional predicates is mapped to PersonHandler.createPerson
|
||||
.POST("/person", handler::createPerson)
|
||||
// otherRoute is a router function that is created elsewhere and added to the route built
|
||||
.add(otherRoute)
|
||||
.build();
|
||||
// end::snippet[]
|
||||
return route;
|
||||
}
|
||||
|
||||
PersonRepository getPersonRepository() {
|
||||
return new PersonRepository() {
|
||||
@Override
|
||||
public Flux<Person> allPeople() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> savePerson(Mono<Person> person) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Person> getPerson(int id) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
RouterFunction<ServerResponse> getOtherRoute() {
|
||||
return RouterFunctions.route().build();
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlerclasses
|
||||
|
||||
data class Person(val name: String)
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlerclasses
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.springframework.http.MediaType.APPLICATION_JSON
|
||||
import org.springframework.web.reactive.function.server.ServerRequest
|
||||
import org.springframework.web.reactive.function.server.ServerResponse
|
||||
import org.springframework.web.reactive.function.server.awaitBody
|
||||
import org.springframework.web.reactive.function.server.bodyAndAwait
|
||||
import org.springframework.web.reactive.function.server.bodyValueAndAwait
|
||||
import org.springframework.web.reactive.function.server.buildAndAwait
|
||||
|
||||
// tag::snippet[]
|
||||
class PersonHandler(private val repository: PersonRepository) {
|
||||
|
||||
// listPeople is a handler function that returns all Person objects found
|
||||
// in the repository as JSON
|
||||
suspend fun listPeople(request: ServerRequest): ServerResponse {
|
||||
val people: Flow<Person> = repository.allPeople()
|
||||
return ServerResponse.ok().contentType(APPLICATION_JSON).bodyAndAwait(people)
|
||||
}
|
||||
|
||||
// createPerson is a handler function that stores a new Person contained
|
||||
// in the request body.
|
||||
// Note that PersonRepository.savePerson(Person) returns Mono<Void>: an empty
|
||||
// Mono that emits a completion signal when the person has been read from the
|
||||
// request and stored. So we use the build(Publisher<Void>) method to send a
|
||||
// response when that completion signal is received (that is, when the Person
|
||||
// has been saved)
|
||||
suspend fun createPerson(request: ServerRequest): ServerResponse {
|
||||
val person = request.awaitBody<Person>()
|
||||
repository.savePerson(person)
|
||||
return ServerResponse.ok().buildAndAwait()
|
||||
}
|
||||
|
||||
// getPerson is a handler function that returns a single person, identified by
|
||||
// the id path variable. We retrieve that Person from the repository and create
|
||||
// a JSON response, if it is found. If it is not found, we use switchIfEmpty(Mono<T>)
|
||||
// to return a 404 Not Found response.
|
||||
suspend fun getPerson(request: ServerRequest): ServerResponse {
|
||||
val personId = request.pathVariable("id").toInt()
|
||||
return repository.getPerson(personId)?.let { ServerResponse.ok().contentType(APPLICATION_JSON).bodyValueAndAwait(it) }
|
||||
?: ServerResponse.notFound().buildAndAwait()
|
||||
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlerclasses
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface PersonRepository {
|
||||
|
||||
fun allPeople(): Flow<Person>
|
||||
|
||||
suspend fun savePerson(person: Person)
|
||||
|
||||
suspend fun getPerson(id: Int): Person?
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlerfilterfunction
|
||||
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.PersonHandler
|
||||
import org.springframework.http.HttpStatus.UNAUTHORIZED
|
||||
import org.springframework.http.MediaType.APPLICATION_JSON
|
||||
import org.springframework.web.reactive.function.server.RouterFunction
|
||||
import org.springframework.web.reactive.function.server.ServerResponse
|
||||
import org.springframework.web.reactive.function.server.buildAndAwait
|
||||
import org.springframework.web.reactive.function.server.coRouter
|
||||
|
||||
class RouterConfiguration {
|
||||
|
||||
fun route(handler: PersonHandler): RouterFunction<ServerResponse> {
|
||||
// tag::snippet[]
|
||||
val securityManager: SecurityManager = getSecurityManager()
|
||||
|
||||
val route = coRouter {
|
||||
("/person" and accept(APPLICATION_JSON)).nest {
|
||||
GET("/{id}", handler::getPerson)
|
||||
GET("/", handler::listPeople)
|
||||
POST("/", handler::createPerson)
|
||||
filter { request, next ->
|
||||
if (securityManager.allowAccessTo(request.path())) {
|
||||
next(request)
|
||||
}
|
||||
else {
|
||||
ServerResponse.status(UNAUTHORIZED).buildAndAwait()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
return route
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun getSecurityManager() = object : SecurityManager {
|
||||
override fun allowAccessTo(path: String): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlerfilterfunction
|
||||
|
||||
interface SecurityManager {
|
||||
|
||||
fun allowAccessTo(path: String): Boolean
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlervalidation
|
||||
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.Person
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.PersonRepository
|
||||
import org.springframework.validation.BeanPropertyBindingResult
|
||||
import org.springframework.validation.Errors
|
||||
import org.springframework.web.reactive.function.server.ServerRequest
|
||||
import org.springframework.web.reactive.function.server.ServerResponse
|
||||
import org.springframework.web.reactive.function.server.awaitBody
|
||||
import org.springframework.web.reactive.function.server.buildAndAwait
|
||||
import org.springframework.web.server.ServerWebInputException
|
||||
|
||||
// tag::snippet[]
|
||||
class PersonHandler(private val repository: PersonRepository) {
|
||||
|
||||
// Create Validator instance
|
||||
private val validator = PersonValidator()
|
||||
|
||||
suspend fun createPerson(request: ServerRequest): ServerResponse {
|
||||
val person = request.awaitBody<Person>()
|
||||
// Apply validation
|
||||
validate(person)
|
||||
repository.savePerson(person)
|
||||
return ServerResponse.ok().buildAndAwait()
|
||||
}
|
||||
|
||||
private fun validate(person: Person) {
|
||||
val errors: Errors = BeanPropertyBindingResult(person, "person")
|
||||
validator.validate(person, errors)
|
||||
if (errors.hasErrors()) {
|
||||
// Raise exception for a 400 response
|
||||
throw ServerWebInputException(errors.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnhandlervalidation
|
||||
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.Person
|
||||
import org.springframework.validation.Errors
|
||||
import org.springframework.validation.Validator
|
||||
|
||||
class PersonValidator : Validator {
|
||||
|
||||
override fun supports(clazz: Class<*>): Boolean {
|
||||
return Person::class.java.isAssignableFrom(clazz)
|
||||
}
|
||||
|
||||
override fun validate(target: Any, errors: Errors) {
|
||||
// Validation logic
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnpredicates
|
||||
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.web.reactive.function.server.RouterFunction
|
||||
import org.springframework.web.reactive.function.server.ServerResponse
|
||||
import org.springframework.web.reactive.function.server.bodyValueAndAwait
|
||||
import org.springframework.web.reactive.function.server.coRouter
|
||||
|
||||
class RouterConfiguration {
|
||||
|
||||
fun route(): RouterFunction<ServerResponse> {
|
||||
// tag::snippet[]
|
||||
val route = coRouter {
|
||||
GET("/hello-world", accept(MediaType.TEXT_PLAIN)) {
|
||||
ServerResponse.ok().bodyValueAndAwait("Hello World")
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
return route
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnrequest
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer
|
||||
import org.springframework.http.codec.multipart.FilePartEvent
|
||||
import org.springframework.http.codec.multipart.FormPartEvent
|
||||
import org.springframework.http.codec.multipart.PartEvent
|
||||
import org.springframework.web.reactive.function.server.ServerRequest
|
||||
import org.springframework.web.reactive.function.server.bodyToFlux
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
class PartEventHandler {
|
||||
|
||||
fun handle(request: ServerRequest) {
|
||||
// tag::snippet[]
|
||||
request.bodyToFlux<PartEvent>().windowUntil(PartEvent::isLast)
|
||||
.concatMap {
|
||||
it.switchOnFirst { signal, partEvents ->
|
||||
if (signal.hasValue()) {
|
||||
val event = signal.get()
|
||||
if (event is FormPartEvent) {
|
||||
val value: String = event.value()
|
||||
// handle form field
|
||||
} else if (event is FilePartEvent) {
|
||||
val filename: String = event.filename()
|
||||
val contents: Flux<DataBuffer> = partEvents.map(PartEvent::content)
|
||||
// handle file upload
|
||||
} else {
|
||||
return@switchOnFirst Mono.error(RuntimeException("Unexpected event: $event"))
|
||||
}
|
||||
} else {
|
||||
return@switchOnFirst partEvents // either complete or error signal
|
||||
}
|
||||
Mono.empty()
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnrequest
|
||||
|
||||
import org.springframework.web.reactive.function.server.ServerRequest
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
class RequestHandler {
|
||||
|
||||
fun bind(request: ServerRequest) {
|
||||
// tag::snippet[]
|
||||
val pet: Mono<Pet> = request.bind(Pet::class.java) { dataBinder -> dataBinder.setAllowedFields("name") }
|
||||
// end::snippet[]
|
||||
}
|
||||
|
||||
data class Pet(val name: String)
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnresponse
|
||||
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.web.reactive.function.server.ServerResponse
|
||||
import org.springframework.web.reactive.function.server.bodyValueWithTypeAndAwait
|
||||
|
||||
class ResponseHandler {
|
||||
|
||||
suspend fun createResponse(): ServerResponse {
|
||||
// tag::snippet[]
|
||||
val person: Person = getPerson()
|
||||
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValueWithTypeAndAwait<Person>(person)
|
||||
// end::snippet[]
|
||||
}
|
||||
|
||||
fun getPerson() = Person("foo")
|
||||
|
||||
data class Person(val name: String)
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webfluxfnroutes
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.Person
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.PersonHandler
|
||||
import org.springframework.docs.web.webfluxfnhandlerclasses.PersonRepository
|
||||
import org.springframework.http.MediaType.APPLICATION_JSON
|
||||
import org.springframework.web.reactive.function.server.RouterFunction
|
||||
import org.springframework.web.reactive.function.server.ServerResponse
|
||||
import org.springframework.web.reactive.function.server.coRouter
|
||||
|
||||
class RouterConfiguration {
|
||||
|
||||
fun routes(): RouterFunction<ServerResponse> {
|
||||
// tag::snippet[]
|
||||
val repository: PersonRepository = getPersonRepository()
|
||||
val handler = PersonHandler(repository)
|
||||
|
||||
val otherRoute: RouterFunction<ServerResponse> = getOtherRoute()
|
||||
|
||||
val route = coRouter {
|
||||
// GET /person/{id} with an Accept header that matches JSON is routed to PersonHandler.getPerson
|
||||
GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
|
||||
// GET /person with an Accept header that matches JSON is routed to PersonHandler.listPeople
|
||||
GET("/person", accept(APPLICATION_JSON), handler::listPeople)
|
||||
// POST /person with no additional predicates is mapped to PersonHandler.createPerson
|
||||
POST("/person", handler::createPerson)
|
||||
// otherRoute is a router function that is created elsewhere and added to the route built
|
||||
}.and(otherRoute)
|
||||
// end::snippet[]
|
||||
return route
|
||||
}
|
||||
}
|
||||
|
||||
fun getOtherRoute() = coRouter { }
|
||||
|
||||
fun getPersonRepository() = object: PersonRepository {
|
||||
override fun allPeople(): Flow<Person> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun savePerson(person: Person) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun getPerson(id: Int): Person? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user