Функциональное программирование в Spring MVC.

Spring 5.0 включил спецификацию ReactiveStreams и представил новый Reactive Stack в качестве альтернативы традиционному стеку сервлетов. И это также принесло разработчикам новую модель функционального программирования, но она поддерживается только в стеке Reactive.

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

В этом посте давайте взглянем на новую функциональную функцию в Spring MVC.

Создайте проект Spring Boot, используя Spring initializr (добавить Интернет, JPA, Ломбока также Н2 стартеры как зависимости.

ПРИМЕЧАНИЕ. Выберите новую версию Spring Boot 2.2.0.BUILD-SNAPSHOT, чтобы получить новую версию Spring 5.2.M1 в ее зависимостях.

Создайте простую сущность JPA Post.

@Data
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
class Post {

    @Id
    @GeneratedValue(strategy = AUTO)
    private Long id;
    private String title;
    private String content;

}

И создайте репозиторий для Post Организация.

interface PostRepository extends JpaRepository<Post, Long> {}

Определите RouterFuncation bean для обработки правил маршрутизации.

@Bean
public RouterFunction<ServerResponse> routes(PostHandler postController) {
    return route(GET("/posts"), postController::all)
        .andRoute(POST("/posts"), postController::create)
        .andRoute(GET("/posts/{id}"), postController::get)
        .andRoute(PUT("/posts/{id}"), postController::update)
        .andRoute(DELETE("/posts/{id}"), postController::delete);
}

Коды почти такие же, как те, которые мы использовали в стеке Reactive, но здесь обратите внимание на ServerRequest, ServerResponse а также RouterFunction импортируются из нового пакета:org.springframework.web.servlet.function.

Давайте посмотрим на детали PostHandler.

@Component
class PostHandler {

    private final PostRepository posts;

    public PostHandler(PostRepository posts) {
        this.posts = posts;
    }

    public ServerResponse all(ServerRequest req) {
        return ServerResponse.ok().body(this.posts.findAll());
    }

    public ServerResponse create(ServerRequest req) throws ServletException, IOException {

        var saved = this.posts.save(req.body(Post.class));
        return ServerResponse.created(URI.create("/posts/" + saved.getId())).build();
    }

    public ServerResponse get(ServerRequest req) {
        return this.posts.findById(Long.valueOf(req.pathVariable("id")))
            .map(post -> ServerResponse.ok().body(post))
            .orElse(ServerResponse.notFound().build());
    }

    public ServerResponse update(ServerRequest req) throws ServletException, IOException {
        var data = req.body(Post.class);

        return this.posts.findById(Long.valueOf(req.pathVariable("id")))
            .map(
                post -> {
                    post.setTitle(data.getTitle());
                    post.setContent(data.getContent());
                    return post;
                }
            )
            .map(post -> this.posts.save(post))
            .map(post -> ServerResponse.noContent().build())
            .orElse(ServerResponse.notFound().build());

    }

    public ServerResponse delete(ServerRequest req) {
        return this.posts.findById(Long.valueOf(req.pathVariable("id")))
            .map(
                post -> {
                    this.posts.delete(post);
                    return ServerResponse.noContent().build();
                }
            )
            .orElse(ServerResponse.notFound().build());
    }

}

Это очень похоже на коды реактивного стека, но методы возвращают ServerResponse вместо Mono<ServerResponse>.

Как и функция RouterFunctionDSL, предоставляемая в стеке Reactive, правила маршрутизации также могут быть написаны на Kotlin DSL.

 router {
        "/posts".nest {
            GET("", postHandler::all)
            GET("{id}", postHandler::get)
            POST("", postHandler::create)
            PUT("{id}", postHandler::update)
            DELETE("{id}", postHandler::delete)
        }

    }

Помимо этого, MockMvc также поддерживает Kotlin DSL, вы можете писать свои тесты в свободном стиле, как показано ниже.

  @Test
  fun `Get all posts should ok`() {
        mockMvc
                .get("/posts") {
                    accept = APPLICATION_JSON
                    headers {
                        contentLanguage = Locale.ENGLISH
                    }
                }
                .andExpect {
                    status { isOk }
                    content { contentType(APPLICATION_JSON_UTF8) }
                    jsonPath("$[0].title") { value(containsString("post")) } 
                }
                .andDo {
                    print()
                }
    }

Проверьте исходные коды из моего Github и сравните его с коды который я написал для демонстрации реактивного стека.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *