HTML Diff
1 added 1 removed
Original 2026-01-01
Modified 2026-02-26
1 <p><strong>Зачем использовать простой фреймворк Javalin на языке Java для обработки запросов и формирования ответов, и как работает шаблонизация.</strong></p>
1 <p><strong>Зачем использовать простой фреймворк Javalin на языке Java для обработки запросов и формирования ответов, и как работает шаблонизация.</strong></p>
2 <blockquote><p>Статью подготовил Сергей Чепурнов, наставник на Хекслете, Java-разработчик с опытом работы более 7 лет в распределенных командах.<a>Профиль Сергея на GitHub</a></p>
2 <blockquote><p>Статью подготовил Сергей Чепурнов, наставник на Хекслете, Java-разработчик с опытом работы более 7 лет в распределенных командах.<a>Профиль Сергея на GitHub</a></p>
3 </blockquote><h2>Содержание</h2>
3 </blockquote><h2>Содержание</h2>
4 <ul><li><a>Как устроены веб-приложения</a></li>
4 <ul><li><a>Как устроены веб-приложения</a></li>
5 <li><a>Как работает маршрутизация в Javalin</a></li>
5 <li><a>Как работает маршрутизация в Javalin</a></li>
6 <li><a>Как работают динамически сформированные запросы</a></li>
6 <li><a>Как работают динамически сформированные запросы</a></li>
7 <li><a>Какими бывают запросы: GET, DELETE, POST, PUT</a></li>
7 <li><a>Какими бывают запросы: GET, DELETE, POST, PUT</a></li>
8 <li><a>Как работает шаблонизация</a></li>
8 <li><a>Как работает шаблонизация</a></li>
9 <li><a>Дополнительные материалы</a></li>
9 <li><a>Дополнительные материалы</a></li>
10 </ul><h2>Как устроены веб-приложения</h2>
10 </ul><h2>Как устроены веб-приложения</h2>
11 <p>Все веб-приложения работают по одному принципу. Во-первых, они состоят из двух частей:</p>
11 <p>Все веб-приложения работают по одному принципу. Во-первых, они состоят из двух частей:</p>
12 <ul><li>Клиент - зачастую это браузер или приложение на устройстве пользователя</li>
12 <ul><li>Клиент - зачастую это браузер или приложение на устройстве пользователя</li>
13 <li>Сервер - компьютер с серверным ПО, который обрабатывает запросы от клиента.</li>
13 <li>Сервер - компьютер с серверным ПО, который обрабатывает запросы от клиента.</li>
14 </ul><p>Во-вторых, в каждом приложении клиент и сервер обмениваются информацией таким образом:</p>
14 </ul><p>Во-вторых, в каждом приложении клиент и сервер обмениваются информацией таким образом:</p>
15 <ul><li>Клиент отправляет запрос к серверу</li>
15 <ul><li>Клиент отправляет запрос к серверу</li>
16 <li>Сервер обрабатывает запрос</li>
16 <li>Сервер обрабатывает запрос</li>
17 <li>Сервер отправляет сформированный ответ обратно клиенту.</li>
17 <li>Сервер отправляет сформированный ответ обратно клиенту.</li>
18 </ul><p>Такая последовательность из трех шагов называется циклом "запрос-обработка-ответ". На схеме она выглядит так:</p>
18 </ul><p>Такая последовательность из трех шагов называется циклом "запрос-обработка-ответ". На схеме она выглядит так:</p>
19 <p>Обработка запросов и формирование ответов - это рутинная задача в каждом веб-приложении.</p>
19 <p>Обработка запросов и формирование ответов - это рутинная задача в каждом веб-приложении.</p>
20 <p>При программировании на языке Java есть два способа работы с циклом "запрос-обработка-ответ":</p>
20 <p>При программировании на языке Java есть два способа работы с циклом "запрос-обработка-ответ":</p>
21 <ol><li><strong>Использование Servlet API</strong>- это стандартный пакет классов и интерфейсов, то есть встроенный механизм построения клиент-серверного приложения по схеме "запрос-ответ". Главный минус - придется писать много однотипного кода, создавать множество классов и т. п.</li>
21 <ol><li><strong>Использование Servlet API</strong>- это стандартный пакет классов и интерфейсов, то есть встроенный механизм построения клиент-серверного приложения по схеме "запрос-ответ". Главный минус - придется писать много однотипного кода, создавать множество классов и т. п.</li>
22 <li><strong>Использование фреймворка</strong>для автоматизации повторяющихся задач. Фреймворк - это каркас веб-приложения, определяющий структуру программы. Код веб-приложения в таком случае легче читается, поддерживается и тестируется. Благодаря фреймворкам вы можете сосредоточиться на логике сайта, не отвлекаясь на продумывание базовой архитектуры или кодирование вспомогательных инструментов.</li>
22 <li><strong>Использование фреймворка</strong>для автоматизации повторяющихся задач. Фреймворк - это каркас веб-приложения, определяющий структуру программы. Код веб-приложения в таком случае легче читается, поддерживается и тестируется. Благодаря фреймворкам вы можете сосредоточиться на логике сайта, не отвлекаясь на продумывание базовой архитектуры или кодирование вспомогательных инструментов.</li>
23 </ol><p>Рассмотрим фреймворк Javalin: он настолько прост, что работающее веб-приложение можно написать буквально в две строчки кода:</p>
23 </ol><p>Рассмотрим фреймворк Javalin: он настолько прост, что работающее веб-приложение можно написать буквально в две строчки кода:</p>
24 <p>Конечно, это еще не все. Также для запуска нужна система сборки - она скачает все указанные зависимости, скомпилирует исходный код и запустит приложение.</p>
24 <p>Конечно, это еще не все. Также для запуска нужна система сборки - она скачает все указанные зависимости, скомпилирует исходный код и запустит приложение.</p>
25 <p>Для примера мы будем использовать систему сборки Gradle. В этом случае запуск приложения выполняется командой run в корне директории проекта:</p>
25 <p>Для примера мы будем использовать систему сборки Gradle. В этом случае запуск приложения выполняется командой run в корне директории проекта:</p>
26 <p>Откроем в браузере страницу с адресом<em>localhost</em></p>
26 <p>Откроем в браузере страницу с адресом<em>localhost</em></p>
27 <p>и посмотрим, запустилось ли наше двухстрочное приложение.</p>
27 <p>и посмотрим, запустилось ли наше двухстрочное приложение.</p>
28 <p>Все прошло успешно:</p>
28 <p>Все прошло успешно:</p>
29 <p>Запрос / и ответ Hello World связаны между собой. Клиент делает запрос по адресу<em>localhost</em></p>
29 <p>Запрос / и ответ Hello World связаны между собой. Клиент делает запрос по адресу<em>localhost</em></p>
30 <p>. При этом сервер понимает, что к нему обратились с запросом "/", и в качестве ответа возвращает строку Hello World.</p>
30 <p>. При этом сервер понимает, что к нему обратились с запросом "/", и в качестве ответа возвращает строку Hello World.</p>
31 <p>Чтобы разобраться подробнее, вернемся к двум строкам выше. Посмотрим, что в них написано:</p>
31 <p>Чтобы разобраться подробнее, вернемся к двум строкам выше. Посмотрим, что в них написано:</p>
32 <p>Первая строка создает объект типа Javalin и устанавливает номер порта, на котором будет работать приложение (порт 7070 в данном случае). Вторая строка добавляет обработчик для запроса /. Обработчик записывается в виде лямбда-выражения и формирует ответ в виде строки.</p>
32 <p>Первая строка создает объект типа Javalin и устанавливает номер порта, на котором будет работать приложение (порт 7070 в данном случае). Вторая строка добавляет обработчик для запроса /. Обработчик записывается в виде лямбда-выражения и формирует ответ в виде строки.</p>
33 <p>Клиент получает ответ от сервера в виде http-ответа, в теле которого содержится строка:</p>
33 <p>Клиент получает ответ от сервера в виде http-ответа, в теле которого содержится строка:</p>
34 <h2>Как работает маршрутизация в Javalin</h2>
34 <h2>Как работает маршрутизация в Javalin</h2>
35 <p>Поступающие запросы нужно маршрутизировать - то есть обработать на сервере. Когда сервер получает запрос от клиента, он начинает маршрутизацию: обрабатывает запрос и определяет, какой ответ нужно отправить клиенту.</p>
35 <p>Поступающие запросы нужно маршрутизировать - то есть обработать на сервере. Когда сервер получает запрос от клиента, он начинает маршрутизацию: обрабатывает запрос и определяет, какой ответ нужно отправить клиенту.</p>
36 <p>Как это происходит:</p>
36 <p>Как это происходит:</p>
37 <ol><li>Сервер считывает<em>/about</em>в конце URL и отличает этот запрос от всех остальных</li>
37 <ol><li>Сервер считывает<em>/about</em>в конце URL и отличает этот запрос от всех остальных</li>
38 <li>Затем сервер формирует подходящий ответ - отправляет html-страницу "О блоге".</li>
38 <li>Затем сервер формирует подходящий ответ - отправляет html-страницу "О блоге".</li>
39 </ol><p>Рассмотрим пример. Создадим веб-приложение в виде блога:<a>https://java-javalin-blog.hexlet.app</a>. Пользователь переходит по ссылкам внутри блога и получает разную информацию. Например, на<a>https://java-javalin-blog.hexlet.app/articles</a>- увидит список всех статей, а на странице<a>https://java-javalin-blog.hexlet.app/about</a>- узнает больше о блоге и авторах.</p>
39 </ol><p>Рассмотрим пример. Создадим веб-приложение в виде блога:<a>https://java-javalin-blog.hexlet.app</a>. Пользователь переходит по ссылкам внутри блога и получает разную информацию. Например, на<a>https://java-javalin-blog.hexlet.app/articles</a>- увидит список всех статей, а на странице<a>https://java-javalin-blog.hexlet.app/about</a>- узнает больше о блоге и авторах.</p>
40 <p>Посмотрим, как маршрутизация работает в приложении:</p>
40 <p>Посмотрим, как маршрутизация работает в приложении:</p>
41 <p>Обратите внимание, что каждому URL соответствует определенный ответ - именно он возвращается в виде html-страницы. Таким образом, пользователь увидит в браузере такую страницу:</p>
41 <p>Обратите внимание, что каждому URL соответствует определенный ответ - именно он возвращается в виде html-страницы. Таким образом, пользователь увидит в браузере такую страницу:</p>
42 <h2>Как работают динамически сформированные запросы</h2>
42 <h2>Как работают динамически сформированные запросы</h2>
43 <p>До этого мы рассматривали статические адреса такого вида:</p>
43 <p>До этого мы рассматривали статические адреса такого вида:</p>
44 <ul><li><a>https://java-javalin-blog.hexlet.app/about</a></li>
44 <ul><li><a>https://java-javalin-blog.hexlet.app/about</a></li>
45 <li><a>https://java-javalin-blog.hexlet.app/articles</a></li>
45 <li><a>https://java-javalin-blog.hexlet.app/articles</a></li>
46 </ul><p>Кроме статических адресов, в веб-приложениях используются и динамически сформированные адреса. Например:</p>
46 </ul><p>Кроме статических адресов, в веб-приложениях используются и динамически сформированные адреса. Например:</p>
47 <ul><li><a>https://java-javalin-blog.hexlet.app/articles/1</a></li>
47 <ul><li><a>https://java-javalin-blog.hexlet.app/articles/1</a></li>
48 <li><a>https://java-javalin-blog.hexlet.app/articles/2</a></li>
48 <li><a>https://java-javalin-blog.hexlet.app/articles/2</a></li>
49 <li><a>https://java-javalin-blog.hexlet.app/articles/3</a></li>
49 <li><a>https://java-javalin-blog.hexlet.app/articles/3</a></li>
50 </ul><p>Такие URL запрашивают статьи по их уникальному идентификатору (id). По id сервер различает запросы и возвращает соответствующую статью в виде html-страницы.</p>
50 </ul><p>Такие URL запрашивают статьи по их уникальному идентификатору (id). По id сервер различает запросы и возвращает соответствующую статью в виде html-страницы.</p>
51 <p>Если на сайте есть список статей, то его можно выводить на странице в виде списка названий, а ссылки формировать динамически и с разными идентификаторами. Обычно шаблон ссылки выглядит следующим образом: “/articles/{id}”, где вместо "{id}” программным путем подставляется конкретный идентификатор статьи.</p>
51 <p>Если на сайте есть список статей, то его можно выводить на странице в виде списка названий, а ссылки формировать динамически и с разными идентификаторами. Обычно шаблон ссылки выглядит следующим образом: “/articles/{id}”, где вместо "{id}” программным путем подставляется конкретный идентификатор статьи.</p>
52 <p>Чтобы обрабатывать такие запросы, на сервере создается отдельный класс контроллер, который отвечает за обработку запроса и формирование ответа клиенту.</p>
52 <p>Чтобы обрабатывать такие запросы, на сервере создается отдельный класс контроллер, который отвечает за обработку запроса и формирование ответа клиенту.</p>
53 <p>Рассмотрим, как выглядит обработка запроса<a>https://java-javalin-blog.hexlet.app/articles/1</a>в коде приложения.</p>
53 <p>Рассмотрим, как выглядит обработка запроса<a>https://java-javalin-blog.hexlet.app/articles/1</a>в коде приложения.</p>
54 <p>Во-первых, в маршрутизации появляется отсылка к методу контроллера:</p>
54 <p>Во-первых, в маршрутизации появляется отсылка к методу контроллера:</p>
55 <p>Эта настройка маршрутизации сообщает о двух важных аспектах:</p>
55 <p>Эта настройка маршрутизации сообщает о двух важных аспектах:</p>
56 <ol><li>GET-запрос по адресу /articles попадает на обработчик ArticleController.listArticles.</li>
56 <ol><li>GET-запрос по адресу /articles попадает на обработчик ArticleController.listArticles.</li>
57 <li>GET-запрос по адресу articles/{id} с любым целочисленным параметром id попадает на обработчик ArticleController.showArticle</li>
57 <li>GET-запрос по адресу articles/{id} с любым целочисленным параметром id попадает на обработчик ArticleController.showArticle</li>
58 - </ol><p>Во-вторых, обработчик запроса вида articles/{id} - метод контроллера, который извлекает параметр id из пути запроса, затем извлекает статью из хранилиа с этим идентификатором и возвращает html-страницу с текстом найденной статьи:</p>
58 + </ol><p>Во-вторых, обработчик запроса вида articles/{id} - метод контроллера, который извлекает параметр id из пути запроса, затем извлекает статью из хранилища с этим идентификатором и возвращает html-страницу с текстом найденной статьи:</p>
59 <p>Извлекать параметры запроса (path param), в данном случае id, можно с помощью фреймворка Javalin:</p>
59 <p>Извлекать параметры запроса (path param), в данном случае id, можно с помощью фреймворка Javalin:</p>
60 <h2>Какими бывают запросы: GET, DELETE, POST, PUT</h2>
60 <h2>Какими бывают запросы: GET, DELETE, POST, PUT</h2>
61 <p>Выше мы рассмотрели, как получить список статей и извлечь одну конкретную статью, - для этого используется метод GET HTTP. Но обычно в блоге можно еще и добавлять и удалять статьи - с этими задачами справляются другие методы HTTP.</p>
61 <p>Выше мы рассмотрели, как получить список статей и извлечь одну конкретную статью, - для этого используется метод GET HTTP. Но обычно в блоге можно еще и добавлять и удалять статьи - с этими задачами справляются другие методы HTTP.</p>
62 <p>Обычно веб-приложение использует четыре метода:</p>
62 <p>Обычно веб-приложение использует четыре метода:</p>
63 <ol><li>GET - получение данных</li>
63 <ol><li>GET - получение данных</li>
64 <li>DELETE - удаление данных</li>
64 <li>DELETE - удаление данных</li>
65 <li>POST - создание данных</li>
65 <li>POST - создание данных</li>
66 <li>PUT - обновление данных.</li>
66 <li>PUT - обновление данных.</li>
67 </ol><p>Для примера попробуем добавить статью в блог. Для этого используем метод POST HTTP:</p>
67 </ol><p>Для примера попробуем добавить статью в блог. Для этого используем метод POST HTTP:</p>
68 <p>Теперь у нас есть форма создания статьи:</p>
68 <p>Теперь у нас есть форма создания статьи:</p>
69 <p>А так выглядит разметка этой формы:</p>
69 <p>А так выглядит разметка этой формы:</p>
70 <p>Сосредоточимся на отправке данных. Сначала пользователь вводит название и текст статьи, а потом нажимает кнопку "Создать". Так формируется запрос POST HTTP:</p>
70 <p>Сосредоточимся на отправке данных. Сначала пользователь вводит название и текст статьи, а потом нажимает кнопку "Создать". Так формируется запрос POST HTTP:</p>
71 <p>А теперь посмотрим, как обработчик POST запроса выглядит в коде:</p>
71 <p>А теперь посмотрим, как обработчик POST запроса выглядит в коде:</p>
72 <p>Всего одной строчкой кода можно получить доступ к данным формы (form param), которые передаются в теле POST-запроса.</p>
72 <p>Всего одной строчкой кода можно получить доступ к данным формы (form param), которые передаются в теле POST-запроса.</p>
73 <p>Чтобы получить две переменные из запроса, требуется две строчки кода в методе- обработчике:</p>
73 <p>Чтобы получить две переменные из запроса, требуется две строчки кода в методе- обработчике:</p>
74 <p>В примерах выше мы рассмотрели:</p>
74 <p>В примерах выше мы рассмотрели:</p>
75 <ul><li>Как извлекать данные из тела POST запроса (form param)</li>
75 <ul><li>Как извлекать данные из тела POST запроса (form param)</li>
76 <li>Как извлекать идентификатор "id" статьи из пути URL (path param), например “/articles/{id}”.</li>
76 <li>Как извлекать идентификатор "id" статьи из пути URL (path param), например “/articles/{id}”.</li>
77 </ul><p>Перейдем к извлечению переменных запроса (query param) из URL. Для этого рассмотрим пример постраничного вывода статей.</p>
77 </ul><p>Перейдем к извлечению переменных запроса (query param) из URL. Для этого рассмотрим пример постраничного вывода статей.</p>
78 <p>В этом случае GET-запрос для запроса 1-ой страницы со статьями выглядит следующим образом:<a>https://java-javalin-blog.hexlet.app/articles?page=1</a></p>
78 <p>В этом случае GET-запрос для запроса 1-ой страницы со статьями выглядит следующим образом:<a>https://java-javalin-blog.hexlet.app/articles?page=1</a></p>
79 <p>В результате данного запроса в браузере можно наблюдать первую страницу со статьями:</p>
79 <p>В результате данного запроса в браузере можно наблюдать первую страницу со статьями:</p>
80 <p>Настроить маршрутизацию для такого запроса можно так:</p>
80 <p>Настроить маршрутизацию для такого запроса можно так:</p>
81 <p>В коде обработчика доступ к переменной запроса (query param) в URL происходит также в одну строчку:</p>
81 <p>В коде обработчика доступ к переменной запроса (query param) в URL происходит также в одну строчку:</p>
82 <h2>Как работает шаблонизация</h2>
82 <h2>Как работает шаблонизация</h2>
83 <p>Представим, что клиент запрашивает страницу с первой статьей<a>https://java-javalin-blog.hexlet.app/articles/1</a>. Тогда сервер получает GET-запрос и присылает в ответ статью в виде html-страницы. Эта страница формируется программным путем с динамическим контентом.</p>
83 <p>Представим, что клиент запрашивает страницу с первой статьей<a>https://java-javalin-blog.hexlet.app/articles/1</a>. Тогда сервер получает GET-запрос и присылает в ответ статью в виде html-страницы. Эта страница формируется программным путем с динамическим контентом.</p>
84 <p>Для этого используется шаблонизатор - инструмент, который позволяет упрощать генерацию конечных html-страниц за счет использования шаблонов. Здесь для примера мы используем Thymeleaf.</p>
84 <p>Для этого используется шаблонизатор - инструмент, который позволяет упрощать генерацию конечных html-страниц за счет использования шаблонов. Здесь для примера мы используем Thymeleaf.</p>
85 <p>В нашем примере код обработчика будет выглядеть так:</p>
85 <p>В нашем примере код обработчика будет выглядеть так:</p>
86 <p>Посмотрим еще раз на две последние строки кода. Здесь вызываются методы контекста приложения:</p>
86 <p>Посмотрим еще раз на две последние строки кода. Здесь вызываются методы контекста приложения:</p>
87 <p>Первый метод - .attribute(). Он записывает объект article как атрибут, к которому можно получить доступ в шаблоне.</p>
87 <p>Первый метод - .attribute(). Он записывает объект article как атрибут, к которому можно получить доступ в шаблоне.</p>
88 <p>Второй метод - .render(). Он производит рендеринг шаблона - то есть записывает динамический контент в html-странице.</p>
88 <p>Второй метод - .render(). Он производит рендеринг шаблона - то есть записывает динамический контент в html-странице.</p>
89 <p>Посмотрим на пример шаблона show.html для вывода одной статьи:</p>
89 <p>Посмотрим на пример шаблона show.html для вывода одной статьи:</p>
90 <p>В этом шаблоне программным путем устанавливаются название статьи и текст самой статьи. Это происходит за счет того, что в шаблоне есть доступ к объекту article, у которого вызываются методы getName() и getDescription().</p>
90 <p>В этом шаблоне программным путем устанавливаются название статьи и текст самой статьи. Это происходит за счет того, что в шаблоне есть доступ к объекту article, у которого вызываются методы getName() и getDescription().</p>
91 <p>В итоге генерируется такая html-страница:</p>
91 <p>В итоге генерируется такая html-страница:</p>
92 <p>Фреймворк Javalin прост в использовании и при этом помогает автоматизировать рутинные задачи при написании веб-приложения. Он идеально подходит для обучения, потому что позволяет сосредоточиться на логике сайта, а не на продумывании базовой архитектуры или кодировании вспомогательных инструментов.</p>
92 <p>Фреймворк Javalin прост в использовании и при этом помогает автоматизировать рутинные задачи при написании веб-приложения. Он идеально подходит для обучения, потому что позволяет сосредоточиться на логике сайта, а не на продумывании базовой архитектуры или кодировании вспомогательных инструментов.</p>
93 <h2>Дополнительные материалы</h2>
93 <h2>Дополнительные материалы</h2>
94 <ul><li><p>Работающая версия блога:<a>https://java-javalin-blog.hexlet.app</a></p>
94 <ul><li><p>Работающая версия блога:<a>https://java-javalin-blog.hexlet.app</a></p>
95 </li>
95 </li>
96 <li><p>Исходный код блога:<a>https://github.com/hexlet-components/java-javalin-blog</a></p>
96 <li><p>Исходный код блога:<a>https://github.com/hexlet-components/java-javalin-blog</a></p>
97 </li>
97 </li>
98 </ul>
98 </ul>