Статьи

Создание веб-приложения с сервлетами Java

Одной из самых приятных особенностей Java является ее богатая, многогранная природа. Конечно, создание традиционных настольных и даже мобильных приложений — это хорошо. Но что, если вы хотите оставить свой текущий фон позади и начать наступать на веб-ландшафт? Хорошая новость заключается в том, что язык поставляется с полноценным Servlet API, который позволяет создавать надежные веб-приложения без особых хлопот.

Вопрос, который должен звучать у вас в голове сейчас: «Как?», Я прав?

Быстрое и грязное введение в сервлеты Java

Введите сервлеты , определенный тип Java-программы, выполняемой в рамках веб-контейнера (также называемые сервлет-контейнерами , Tomcat и Jetty являются основными примерами), которая позволяет обрабатывать запросы клиентов и ответы сервера простым и эффективным способом. Это не место и не время, чтобы скучать вам до академических определений о том, что такое сервлет. Достаточно сказать, что сервлеты создаются и уничтожаются своими серверами, а не разработчиком, и действуют как промежуточный уровень между клиентами (обычно веб-браузерами) и другими приложениями, работающими на сервере (например, базами данных).

Сервлеты являются мощными существами, которые, помимо прочего, могут получать данные от клиента, как правило, с помощью запросов GET и POST, обрабатывать файлы cookie и параметры сеанса, обрабатывать данные с помощью дополнительных прикладных уровней и отправлять выходные данные клиенту в обоих случаях. текстовые и двоичные форматы (HTML, XML, PDF, JPG, GIF и т. д.), во многих случаях с использованием файла Java Server Pages (JSP).

Лучший способ начать использовать сервлеты — на конкретном примере. Таким образом, в следующих нескольких строках я собираюсь создать простое веб-приложение, которое позволит клиентам регистрироваться с помощью простой HTML-формы. Данные, представленные клиентами, будут собираться сервлетом и проверяться на базовом уровне с помощью статических помощников.

Использование модели клиент-сервер HTTP

Чтобы иметь четкое представление о том, как будет структурировано клиентское приложение, вот как это будет сделано:

java_servlet_application_layout

Ничего неожиданного, правда? Но обо всем по порядку.

Первый шаг, который мы должны сделать для создания приложения, — это определить так называемый дескриптор развертывания (DD). Как следует из названия, оно определяет, как приложение должно быть развернуто в конкретной среде. Неудивительно, что когда речь идет о веб-приложениях, дескриптор развертывания представляет собой простой XML-файл с именем web.xml и является частью стандартной спецификации Java EE. В данном конкретном случае это будет выглядеть так:

 <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" metadata-complete="false"> </web-app> 

Как показано выше, web.xml вообще не содержит директив и определяет только версию Спецификации сервлета Java (3.1), которую мы будем использовать в приложении. Конечно, он может содержать гораздо больше контента, включая директивы отображения сервлетов , параметры инициализации , список файлов приветствия и несколько дополнительных настроек. Но, чтобы весь процесс разработки был простым, я сохраню файл таким простым.

Поскольку образец приложения использует API сервлетов, а также использует страницы сервера Java для отображения данных о клиентах, нам необходимо получить все необходимые JAR-файлы. Maven сделает за нас тяжелую работу, поэтому вот как будет определен файл POM:

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.electricalweb.customerapplication</groupId> <artifactId>customerapplication</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>Customer Application</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.el</groupId> <artifactId>el-api</artifactId> <version>2.2</version> </dependency> </dependencies> <build> <finalName>Customer Application</finalName> </build> </project> 

В двух словах, файл pom.xml определяет все зависимости, необходимые для работы с сервлетами: JSP, Стандартная библиотека тегов Java (JSTL) и Язык выражений Java (JEL).

На данный момент нам удалось создать файлы конфигурации приложения клиента. Однако в своем текущем состоянии приложение буквально ничего не делает. Хлоп! Поскольку основная цель здесь — разрешить клиентам регистрироваться с использованием формы HTML, следующее, что нам нужно сделать, — это определить файлы JSP, которым поручено отображать вышеупомянутую форму и отображать данные клиента после успешного завершения процесса регистрации. Это именно та тема, которая рассматривается в следующем разделе.

Создание регистрации и приветствия

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

Вот первый файл JSP:

 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Customer Sign Up</title> </head> <body> <h1>Customer Sign Up</h1> <c:if test="${violations != null}"> <c:forEach items="${violations}" var="violation"> <p>${violation}.</p> </c:forEach> </c:if> <form action="${pageContext.request.contextPath}/processcustomer" method="post"> <label for="firstname">First Name : </label> <input type="text" name="firstname" id="firstname" value="${firstname}"> <label for="lastname">Last Name:</label> <input type="text" name="lastname" id="lastname" value="${lastname}"> <label for="email">Email: </label> <input type="text" name="email" id="email" value="${email}"> <input type="submit" name="signup" value="Sign Up"> </form> </body> </html> 

Файл содержит простой HTML с несколькими настройками. В этом прелесть технологии JSP в сочетании с JSTL и JEL. Обратите внимание, как легко проверить нарушения валидации и зациклить их в верхней части файла, используя стандартные теги, такие как <c:if> и <c:forEach> .

Атрибут action формы регистрации указывает на следующий URL: ${pageContext.request.contextPath}/processcustomer . Короче говоря, это означает, что каждый раз, когда клиент пытается зарегистрироваться, данные будут отправляться processcustomer процесса независимо от контекстного пути приложения. Поэтому независимо от того, по какому URL-адресу будет доступна форма регистрации, URL-адрес действия всегда будет соответствовать этому. Это достигается благодаря функциональности объектов, доступных из файла JSP, таких как request .

Ниже мы увидим, как сервлет отображается на URL processcustomer процесса и как он контролирует введенные данные. Но прежде чем я определю соответствующий сервлет, вот файл JSP, который отображает страницу welcome :

 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Customer Data</title> </head> <body> <h1>Thanks for signing up with us!</h1> <h2>You provided the following data:</h2> <p><strong>First Name:</strong> ${firstname}</p> <p><strong>Last Name:</strong> ${lastname}</p> <p><strong>Email: </strong>${email}</p> </body> </html> 

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

Построение контроллера клиента

Поверьте, определение сервлета, способного захватывать данные, введенные в предыдущей форме регистрации, очень просто. Все, что нам нужно сделать, это создать подкласс собственного класса HttpServlet и реализовать его методы doGet() или doPost() (или оба при необходимости). В этом конкретном случае сервлет клиента будет перехватывать данные, поступающие из запросов POST.

Его определение таково:

 @WebServlet(name = "CustomerController", urlPatterns = "/processcustomer") public class CustomerController extends HttpServlet { @Override protected void doPost( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestCustomer customer = RequestCustomer.fromRequestParameters(request); customer.setAsRequestAttributes(request); List<String> violations = customer.validate(); if (!violations.isEmpty()) { request.setAttribute("violations", violations); } String url = determineUrl(violations); request.getRequestDispatcher(url).forward(request, response); } private String determineUrl(List<String> violations) { if (!violations.isEmpty()) { return "/"; } else { return "/WEB-INF/views/customerinfo.jsp"; } } private static class RequestCustomer { private final String firstName; private final String lastName; private final String email; private RequestCustomer(String firstName, String lastName, String email) { this.firstName = firstName; this.lastName = lastName; this.email = email; } public static RequestCustomer fromRequestParameters( HttpServletRequest request) { return new RequestCustomer( request.getParameter("firstname"), request.getParameter("lastname"), request.getParameter("email")); } public void setAsRequestAttributes(HttpServletRequest request) { request.setAttribute("firstname", firstName); request.setAttribute("lastname", lastName); request.setAttribute("email", email); } public List<String> validate() { List<String> violations = new ArrayList<>(); if (!StringValidator.validate(firstName)) { violations.add("First Name is mandatory"); } if (!StringValidator.validate(lastName)) { violations.add("Last Name is mandatory"); } if (!EmailValidator.validate(email)) { violations.add("Email must be a well-formed address"); } return violations; } } } 

Первое, на что следует обратить внимание, это использование @WebServlet(name = "CustomerController", urlPatterns = "/processcustomer") . Он указывает контейнеру сервлета использовать класс CustomerController для обработки HTTP-запросов к URL-адресу /processcustomer . Можно добиться того же поведения, включив директивы отображения сервлета в web.xml , как показано здесь , но, поскольку я использую спецификацию 3.1 сервлета, нет необходимости прибегать к директивам отображения в дескрипторе развертывания. Просто простые аннотации уровня класса обрабатывают процесс отображения для нас.

В этом случае я назвал сервлет CustomerController как цепляюсь за базовую схему Model — View — Controller, поэтому сервлет ведет себя как обычный контроллер действий. Тем не менее, можно назвать это как-нибудь еще, если соглашение об именах является единым для всего приложения. Как правило, рекомендуется использовать имя класса сервлета в качестве значения для @WebServlet аннотации @WebServlet . В противном случае некоторые контейнеры сервлетов не смогут выполнить сопоставление и выдают уродливую ошибку HTTP status 404, сообщающую, что запрошенный ресурс (сам сервлет) недоступен.

Тем не менее, сам класс CustomerController выполняет несколько простых задач. Прежде всего, он собирает данные, введенные в форму регистрации, с помощью реализации собственного интерфейса HttpServletRequest , который содержит значения, соответствующие полям имени, lastname и email формы регистрации. Во-вторых, он устанавливает эти значения в качестве атрибутов запроса, чтобы их можно было повторно отображать либо в представлении формы регистрации, либо в представлении результатов. Наконец, данные проверяются с помощью нескольких статических помощников, которые проверяют наличие пустых строк и правильных адресов электронной почты.

Сами валидаторы являются простыми классами, проверяющими пару желаемых свойств, например, является ли имя непустым и адрес электронной почты выглядит, ну, в общем, как адрес электронной почты. Если вы хотите узнать, как они реализованы, вы можете посмотреть их здесь .

Результат процесса проверки в основном контролирует поток приложения: если данные недействительны, клиент перенаправляется через объект диспетчера запросов в представление формы регистрации, и ошибки тут же отображаются. В противном случае страница welcome отображается, так как клиент успешно зарегистрировался.

На данный момент мы создали полнофункциональное веб-приложение, которое позволяет клиентам регистрироваться с помощью формы HTML, базового сервлета и пары файлов JSP. Возможно, самой большой ошибкой здесь является использование статических помощников для проверки данных клиента вместо обращения к стандартному подходу, такому как проверка объектов домена и даже целых графов объектов с использованием Java Bean Validation . Детали того, как это сделать, останутся темой следующей статьи.

Запуск приложения

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

  1. В качестве предварительных условий вам понадобятся Git (обязательно выберите версию, которая работает с вашей операционной системой), Maven и контейнер сервлета (среди прочего, Apache Tomcat , Jetty или JBoss Wildfly ). Возможно, ваша IDE поставляется с одной или несколькими из этих систем, и в этом случае вы можете использовать встроенные версии.

  2. Используйте Git, чтобы клонировать репозиторий приложений на локальный компьютер и импортировать его в IDE, предпочтительно в виде проекта Maven.

  3. Разверните весь проект в контейнер сервлета и запустите его. Развертывание проекта означает создание файла WAR (веб-архива) или разнесенного WAR (также известного как развернутое развертывание ) и размещение его в папке развертывания контейнера сервлета по умолчанию. Во многих случаях ваша IDE будет развертывать проект автоматически для вас, когда вы нажимаете кнопку « Выполнить» , так что не стоит портить себе жизнь, пытаясь выполнить эту задачу вручную, если вам не нужно. Подробности смотрите в документации вашей IDE (здесь это для IntelliJ IDEA . Как только проект будет правильно развернут, и если все пойдет правильно, откроется браузер по умолчанию, и вы увидите форму регистрации клиента.

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

Не стесняйтесь погладить себя по спине. Поздравляем! Вы разработали полнофункциональное веб-приложение на Java.

Последние мысли

К этому моменту вы приобрели все навыки, необходимые для создания собственного веб-приложения на Java, и, что самое главное, вообще не прибегая к сложностям какой-либо среды. Все, что вам нужно, это использовать API Servlet , использовать какую-то технологию рендеринга, такую ​​как JSP и обычная Java. Разве это не здорово?

Стоит отметить, что реализация класса CustomerController подчеркивает достоинства и недостатки, проявляемые сервлетами: с одной стороны, это показывает, в двух словах, как легко обрабатывать параметры запроса и отправлять ответы обратно клиенту в различных форматах. Но эта функциональность имеет свою цену: обе реализации интерфейсов HttpServletRequest и HttpServletResponse представляют собой простые сервисные локаторы . Это по сути не плохо, так как локаторы — это просто держатели данных. К сожалению, всякий раз, когда вы добавляете сервлет в область своего приложения, к нему присоединяются эти реализации.

Если вы хотите попробовать приложение для клиента и поэкспериментировать с ним, просто клонируйте репозиторий из GitLab . Не стесняйтесь оставлять мне свои комментарии. Я постараюсь ответить как можно скорее.