25 changed files with 1032 additions and 363 deletions
@ -0,0 +1,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) { } |
||||
@ -0,0 +1,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[]
|
||||
|
||||
@ -0,0 +1,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); |
||||
} |
||||
@ -0,0 +1,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; |
||||
} |
||||
} |
||||
@ -0,0 +1,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); |
||||
} |
||||
@ -0,0 +1,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[]
|
||||
@ -0,0 +1,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
|
||||
} |
||||
} |
||||
@ -0,0 +1,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; |
||||
} |
||||
} |
||||
@ -0,0 +1,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[]
|
||||
} |
||||
} |
||||
@ -0,0 +1,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) { } |
||||
|
||||
} |
||||
@ -0,0 +1,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) { } |
||||
} |
||||
@ -0,0 +1,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(); |
||||
} |
||||
} |
||||
@ -0,0 +1,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) |
||||
@ -0,0 +1,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[] |
||||
@ -0,0 +1,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? |
||||
} |
||||
@ -0,0 +1,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") |
||||
} |
||||
} |
||||
@ -0,0 +1,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 |
||||
} |
||||
@ -0,0 +1,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[] |
||||
|
||||
@ -0,0 +1,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 |
||||
} |
||||
} |
||||
@ -0,0 +1,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 |
||||
} |
||||
} |
||||
@ -0,0 +1,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[] |
||||
} |
||||
} |
||||
@ -0,0 +1,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) |
||||
} |
||||
@ -0,0 +1,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) |
||||
} |
||||
@ -0,0 +1,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") |
||||
} |
||||
} |
||||
Loading…
Reference in new issue