Переменные пути Thymeleaf с загрузкой Spring
Вступление
Thymeleaf – механизм создания шаблонов, используемый многими разработчиками программного обеспечения Java в веб-приложениях на базе Spring. Важной особенностью любого веб-приложения является поддержка динамических URL-адресов и переменных пути внутри этих URL-адресов.
Большинство API-интерфейсов REST широко использует переменные пути для указания идентификаторов элементов, с которыми они выполняют операции. Типичный пример:
https://www.somewebsite.com/viewPost/path-variables-with-spring-boot
# OR
https://www.somewebsite.com/viewProduct/5
В обоих случаях мы пытаемся найти ресурс, обозначенный определенным идентификатором. В первом случае мы идентифицируем ресурс по заголовку path-variables-with-spring-boot, а во втором мы идентифицируем его с помощью идентификатора 5.
В этом уроке мы рассмотрим, как использовать Thymeleaf для извлечения переменных пути и как использовать контроллеры загрузки Spring для их обработки.
На протяжении всего урока мы будем использовать модель Post, запись в блоге, в которой есть идентификатор и какой-то контент:
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_sequence")
private Long id;
private String content;
// Constructors, Getters, Setters, and toString
}
Поскольку мы используем загрузку Spring, давайте ещё и загрузим пострепозицию на основе JpaRepository, которая позволяет нам выполнять операции CRUD:
@Repository
public interface PostRepository extends JpaRepository<Post, Long> {}
Переменные пути Thymeleaf с загрузкой Spring
Некоторые URL-адреса являются динамическими. Учитывая тот факт, что мы можем создавать, читать, обновлять и удалять объекты публикации, нам понадобятся динамические URL-адреса для запросов на ПОЛУЧЕНИЕ (GET), ОБНОВЛЕНИЕ (UPDATE) и УДАЛЕНИЕ (DELETE).
Начнём наш проект с нескольких постов:
@GetMapping("/initialize")
public ResponseEntity<String> initialize() {
Post post1 = new Post("Content of post 1");
Post post2 = new Post("Content of post 2");
Post post3 = new Post("Content of post 3");
postRepository.saveAll(List.of(post1, post2, post3));
return ResponseEntity.ok("Initialized posts");
}
Как только мы достигнем конечной точки инициализации (/initialize), будут сгенерированы и сохранены в базе данных три поста. Теперь давайте определим статический URL-адрес для пользователя, чтобы получать все сообщения:
@GetMapping("/viewPosts")
public String viewAllPostsAndComments(Model model){
List<Post> postList = postRepository.findAll();
model.addAttribute("postList", postList);
return "all-posts";
}
Это статический URL-адрес и обработчик: нет никаких переменных пути или параметров, которые могли бы позволить пользователю влиять на то, какие сообщения извлекаются. Обычно это вам и нужно, если хотите, чтобы пользователь мог выбирать, куда перемещаться. После выбора они могут просмотреть подробный список любого поста, просто щёлкнув по нему и перейдя на страницу.
Поскольку было бы непрактично (и нереально) создавать обработчик запросов для каждой записи, мы можем создать динамический обработчик, который принимает любой идентификатор записи, находит запись в базе данных и выводит ее:
@GetMapping("/viewPost/{postId}")
public String viewPost(@PathVariable("postId") Long postId, Model model) {
Post post = postRepository.findById(postId).get();
model.addAttribute("post", post);
return "view-post";
}
Так мы описали @GetMapping для URL/viewpost/{postID}. postId, заключенный в фигурные скобки, – это динамическая переменная, которая может присвоить любое значение и/или тип. В подписи нашего метода мы использовали аннотацию @PathVariable, установив имя переменной path и присвоив ей тип, на который мы можем ссылаться, – Long postID.
Как только запускается запрос GET, на конечной точке /viewpost/5, postId преобразуется в Long, и мы можем использовать его для поиска сообщения по его идентификатору в базе данных. Однако если мы передадим другой тип, например, String
– /viewPost/some-post
, контролёр выдаст исключение:
java.lang.NumberFormatException: For input string: "some-post"
Поскольку у нас есть контролёры для обработки как запроса на просмотр всех сообщений, так и для просмотра одного, давайте быстро напишем страницы Thymeleaf, которые позволят нам перейти к этим обработчикам запросов. Давайте сначала начнём со страницы, на которой перечислены все сообщения и которая позволяет пользователю переходить к различным обработчикам запросов контролёра:
<div th:each="post : ${postList}">
<p th:text="${post.content}"></p>
<a class="btn btn-info" th:href="@{/viewPost/{id}(id = ${post.id})}">View Post</a>
</div>
Здесь мы повторили каждое сообщение из postList (список сообщений, которые мы добавили в экземпляр модели), и для каждого из них добавили кнопку, позволяющую пользователю просматривать сообщение. Атрибут href приводит нас к:
th:href="@{/viewPost/{id}(id = ${post.id})}
Это стандартный синтаксис URL-адреса для добавления параметров в Thymeleaf. Стандартное выражение @{}, используемое для ссылок, также принимает переменные пути. {id} – это переменная пути, которую мы можем установить извне. Здесь мы указали на post.id. После визуализации на стороне сервера это выражение оценивается так:
<a href="localhost:8080/viewPost/1" class="btn btn-info" >View Post</a>
Нажав на кнопку, вы запустите конечную точку /viewpost/{postID}, при этом значение postId будет преобразовано в 1, а пострепозитарий получит сообщение с идентификатором 1, возвращая его к view-post:
<div class="container">
<div class="row">
<div class="col-md-12 bg-light">
<p th:text="${post.content}"></p>
</div>
</div>
</div>
Заключение
Переменные пути являются ключевой особенностью API-интерфейсов REST. В этом руководстве мы рассмотрели, как использовать переменные пути в Spring Boot с помощью Thymeleaf.