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>