Выбрать главу

В качестве простого примера, который объединяет все эти типы, рассмотрим следующий пример Hello World:

package demo;

import static org.springframework.web.

        reactive.function.server.RequestPredicates.GET;

import static org.springframework.web.

        reactive.function.server.RouterFunctions.route;

import static org.springframework.web.

        reactive.function.server.ServerResponse.ok;

import static reactor.core.publisher.Mono.just;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.reactive.function.server.RouterFunction;

 

@Configuration

public class RouterFunctionConfig {

    @Bean

    public RouterFunction<?> helloRouterFunction() {

        return route(GET("/hello"),

            request -> ok().body(just("Hello World!"), String.class));

    }

}

Первое, на что нужно обратить внимание, это то, что вы произвели статический импорт нескольких вспомогательных классов, которые вы можете использовать для создания вышеупомянутых функциональных типов. Вы также статически импортировали Mono, чтобы остальную часть кода было легче читать и понимать.

В этом классе аннотированном как @Configuration у вас есть один метод @Bean типа RouterFunction <?>. Как уже упоминалось, RouterFunction объявляет сопоставления между одним или несколькими объектами RequestPredicate и функциями, которые будут обрабатывать соответствующие запрос(ы).

Метод route() из RouterFunctions принимает два параметра: RequestPredicate и функцию для обработки совпадающих запросов. В этом случае метод GET() из RequestPredicates объявляет RequestPredicate, который совпадает с HTTP-запросами GET для пути /hello.

Что касается функции-обработчика, она написана как лямбда, хотя она также может быть ссылкой на метод. Хотя это явно не объявлено, лямбда-обработчик принимает ServerRequest в качестве параметра. Он возвращает ServerResponse, используя ok() из ServerResponse и body() из BodyBuilder, который был возвращен из ok(). Это было сделано для того, чтобы создать ответ с кодом состояния HTTP 200 (ОК) и полезной нагрузкой body с надписью Hello World!

Метод helloRouterFunction() объявляет RouterFunction, которая обрабатывает только один вид запроса. Но если вам нужно обработать запрос другого типа, вам не нужно писать другой метод @Bean, хотя вы можете это сделать. Вам нужно только вызвать andRoute(), чтобы объявить другое сопоставление RequestPredicate-to-function. Например, вот как вы можете добавить другой обработчик для запросов GET для /bye:

@Bean

public RouterFunction<?> helloRouterFunction() {

    return route(GET("/hello"),

            request -> ok().body(just("Hello World!"), String.class))

        .andRoute(GET("/bye"),

            request -> ok().body(just("See ya!"), String.class));

}

Hello World примеры отлично подходят для знакомства с чем-то новым. Но давайте немного расширим этот пример и посмотрим, как использовать функциональную модель веб-программирования Spring для обработки запросов, которые напоминают реальные сценарии.

Чтобы продемонстрировать, как функциональная модель программирования может использоваться в реальном приложении, давайте заново напишем функционал DesignTacoController в функциональном стиле. Следующий класс конфигурации является функциональным аналогом DesignTacoController:

@Configuration

public class RouterFunctionConfig {

    @Autowired

    private TacoRepository tacoRepo;

    @Bean

    public RouterFunction<?> routerFunction() {

        return route(GET("/design/taco"), this::recents)

            .andRoute(POST("/design"), this::postTaco);

    }

    public Mono<ServerResponse> recents(ServerRequest request) {

        return ServerResponse.ok()

            .body(tacoRepo.findAll().take(12), Taco.class);

    }

    public Mono<ServerResponse> postTaco(ServerRequest request) {

        Mono<Taco> taco = request.bodyToMono(Taco.class);

        Mono<Taco> savedTaco = tacoRepo.save(taco);

        return ServerResponse

            .created(URI.create(

                "http://localhost:8080/design/taco/" +

                savedTaco.getId()))

            .body(savedTaco, Taco.class);

    }

}

Как вы можете видеть, метод routerFunction() объявляет bean RouterFunction<?> как в примере Hello World. Но это зависит от того, какие типы запросов обрабатываются и как они обрабатываются. Но он отличается тем, какие типы запросов обрабатываются и как они обрабатываются. В этом случае функция маршрутизатора создается для обработки запросов GET для /design/taco и POST для /design.

Маршруты обрабатываются ссылками на методы. Лямбды хороши, когда поведение функции RouterFunction относительно простое и краткое. Во многих случаях, однако, лучше извлечь эту функциональность в отдельный метод (или даже в отдельный метод в отдельном классе), чтобы поддерживать читаемость кода.

Запросы GET на /design/taco будут обрабатываться методом recents(). Он использует внедренный TacoRepository для извлечения Mono<Taco>, из которого он берет 12 элементов. А запросы POST для /design обрабатываются методом postTaco(), который извлекает Mono<Taco> из входящего ServerRequest. Затем метод postTaco() использует TacoRepository, чтобы сохранить его, прежде чем вернуть Mono<Taco>, который возвращающийся из метода save().

11.3 Тестирование реактивных контроллеров