Перейти к основному содержимому

Введение в дубли

· 7 мин. чтения

1. Обзор

В экосистеме Java доступно множество веб-фреймворков, таких как Spring , Play и Grails . Однако ни один из них не может претендовать на то, чтобы быть полностью неизменным и объектно-ориентированным.

В этом руководстве мы рассмотрим платформу Takes и создадим простое веб-приложение, используя его общие функции, такие как маршрутизация, обработка запросов/ответов и модульное тестирование.

2. Берет

Takes — это неизменяемый веб-фреймворк Java 8, который не использует ни null , ни общедоступные статические методы.

Кроме того, фреймворк не поддерживает изменяемые классы, кастинг или отражение . Следовательно, это настоящая объектно-ориентированная структура.

Takes не требуют конфигурационных файлов для установки. Кроме того, он предоставляет встроенные функции, такие как ответ JSON/XML и шаблоны.

3. Настройка

Во-первых, мы добавим последнюю версию Maven-зависимости в pom.xml : ``

<dependency>
<groupId>org.takes</groupId>
<artifactId>takes</artifactId>
<version>1.19</version>
</dependency>

Затем создадим класс TakesHelloWorld , реализующий интерфейс Take :

public class TakesHelloWorld implements Take {
@Override
public Response act(Request req) {
return new RsText("Hello, world!");
}
}

Интерфейс Take обеспечивает фундаментальную функцию фреймворка. Каждый Take служит обработчиком запроса, возвращая ответ через метод действия .

Здесь мы использовали класс RsText для рендеринга простого текста Hello, world! в качестве ответа, когда делается запрос к дублю TakesHelloWorld .

Далее мы создадим класс TakesApp для запуска веб-приложения:

public class TakesApp {
public static void main(String... args) {
new FtBasic(new TakesHelloWorld()).start(Exit.NEVER);
}
}

Здесь мы использовали класс FtBasic , который обеспечивает базовую реализацию интерфейса Front для запуска веб-сервера и пересылки запроса дублю TakesHelloWorld .

Takes реализует свой собственный веб-сервер без сохранения состояния с помощью класса ServerSocket . По умолчанию он запускает сервер на порту 80. Однако мы можем определить порт в коде:

new FtBasic(new TakesHelloWorld(), 6060).start(Exit.NEVER);

Или мы можем передать номер порта, используя параметр командной строки –port .

Затем давайте скомпилируем классы с помощью команды Maven: ~

~

mvn clean package

Теперь мы готовы запустить класс TakesApp как простое Java-приложение в среде IDE.

4. Беги

Мы также можем запустить наш класс TakesApp как отдельное приложение веб-сервера.

4.1. Командная строка Java

Во-первых, давайте скомпилируем наши классы:

javac -cp "takes.jar:." com.foreach.takes.*

Затем мы можем запустить приложение с помощью командной строки Java:

java -cp "takes.jar:." com.foreach.takes.TakesApp --port=6060

4.2. Мавен

Или мы можем использовать плагин exec-maven- plugin , чтобы запустить его через Maven `` :

<profiles>
<profile>
<id>reload</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>start-server</id>
<phase>pre-integration-test</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.foreach.takes.TakesApp</mainClass>
<cleanupDaemonThreads>false</cleanupDaemonThreads>
<arguments>
<argument>--port=${port}</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>

Теперь мы можем запустить наше приложение с помощью команды Maven:

mvn clean integration-test -Preload -Dport=6060

5. Маршрутизация

Фреймворк предоставляет класс TkFork для маршрутизации запросов к разным дублям.

Например, добавим в наше приложение несколько маршрутов:

public static void main(String... args) {
new FtBasic(
new TkFork(
new FkRegex("/", new TakesHelloWorld()),
new FkRegex("/contact", new TakesContact())
), 6060
).start(Exit.NEVER);
}

Здесь мы использовали класс FkRegex для сопоставления пути запроса.

6. Обработка запросов

Платформа предоставляет несколько классов-декораторов в пакете org.takes.rq для обработки HTTP-запросов.

Например, мы можем использовать интерфейс RqMethod для извлечения метода HTTP:

public class TakesHelloWorld implements Take { 
@Override
public Response act(Request req) throws IOException {
String requestMethod = new RqMethod.Base(req).method();
return new RsText("Hello, world!");
}
}

Точно так же интерфейс RqHeaders доступен для получения заголовков запроса:

Iterable<String> requestHeaders = new RqHeaders.Base(req).head();

Мы можем использовать класс RqPrint для получения тела запроса:

String body = new RqPrint(req).printBody();

Точно так же мы можем использовать класс RqFormSmart для доступа к параметру формы:

String username = new RqFormSmart(req).single("username");

7. Обработка ответов

Takes также предоставляет множество полезных декораторов для обработки ответа HTTP в пакете org.takes.rs .

Декоратор ответа реализует методы head и body интерфейса Response .

Например, класс RsWithStatus отображает ответ с кодом состояния:

Response resp = new RsWithStatus(200);

Вывод ответа можно проверить с помощью метода head :

assertEquals("[HTTP/1.1 200 OK], ", resp.head().toString());

Точно так же класс RsWithType отображает ответ с типом содержимого:

Response resp = new RsWithType(new RsEmpty(), "text/html");

Здесь класс RsEmpty отображает пустой ответ.

Точно так же мы можем использовать класс RsWithBody для рендеринга ответа с телом.

Итак, давайте создадим класс TakesContact и воспользуемся обсуждаемыми декораторами для рендеринга ответа:

public class TakesContact implements Take {
@Override
public Response act(Request req) throws IOException {
return new RsWithStatus(
new RsWithType(
new RsWithBody("Contact us at https://www.foreach.com"),
"text/html"), 200);
}
}

Точно так же мы можем использовать класс RsJson для отображения ответа JSON:

@Override 
public Response act(Request req) {
JsonStructure json = Json.createObjectBuilder()
.add("id", rs.getInt("id"))
.add("user", rs.getString("user"))
.build();
return new RsJson(json);
}

8. Обработка исключений

Платформа содержит интерфейс Fallback для обработки исключительных ситуаций. Он также предоставляет несколько реализаций для обработки резервных сценариев.

Например, давайте воспользуемся классом TkFallback для обработки HTTP 404 и отображения сообщения пользователю:

public static void main(String... args) throws IOException, SQLException {
new FtBasic(
new TkFallback(
new TkFork(
new FkRegex("/", new TakesHelloWorld()),
// ...
),
new FbStatus(404, new RsText("Page Not Found"))), 6060
).start(Exit.NEVER);
}

Здесь мы использовали класс FbStatus для обработки резервного варианта для определенного кода состояния.

Точно так же мы можем использовать класс FbChain для определения комбинации запасных вариантов:

new TkFallback(
new TkFork(
// ...
),
new FbChain(
new FbStatus(404, new RsText("Page Not Found")),
new FbStatus(405, new RsText("Method Not Allowed"))
)
), 6060
).start(Exit.NEVER);

Кроме того, мы можем реализовать интерфейс Fallback для обработки исключений:

new FbChain(
new FbStatus(404, new RsText("Page Not Found")),
new FbStatus(405, new RsText("Method Not Allowed")),
new Fallback() {
@Override
public Opt<Response> route(RqFallback req) {
return new Opt.Single<Response>(new RsText(req.throwable().getMessage()));
}
}
)

9. Шаблоны

Давайте интегрируем Apache Velocity с нашим веб-приложением Takes, чтобы обеспечить некоторые функции шаблонов.

Во- первых, мы добавим зависимость Maven от speed -engine-core :

<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>

Затем мы будем использовать класс RsVelocity для определения строки шаблона и параметров привязки в методе действия :

public class TakesIndex implements Take {
@Override
public Response act(Request req) throws IOException {
return new RsHtml(
new RsVelocity("${username}", new RsVelocity.Pair("username", "ForEach")));
);
}
}

Здесь мы использовали класс RsHtml для отображения HTML-ответа.

Также мы можем использовать шаблон скорости с классом RsVelocity :

new RsVelocity(this.getClass().getResource("/templates/index.vm"), 
new RsVelocity.Pair("username", username))
);

10. Модульное тестирование

Фреймворк поддерживает модульное тестирование любого Take , предоставляя класс RqFake , который создает поддельный запрос:

Например, давайте напишем модульный тест для нашего класса TakesContact , используя JUnit :

String resp = new RsPrint(new TakesContact().act(new RqFake())).printBody();
assertEquals("Contact us at https://www.foreach.com", resp);

11. Интеграционное тестирование

Мы можем протестировать все приложение с помощью JUnit и любого HTTP-клиента.

Фреймворк предоставляет класс FtRemote , который запускает сервер на произвольном порту и обеспечивает удаленное управление выполнением Take .

Например, давайте напишем интеграционный тест и проверим ответ класса TakesContact :

new FtRemote(new TakesContact()).exec(
new FtRemote.Script() {
@Override
public void exec(URI home) throws IOException {
HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(new HttpGet(home));
int statusCode = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity);

assertEquals(200, statusCode);
assertEquals("Contact us at https://www.foreach.com", result);
}
});

Здесь мы использовали Apache HttpClient для выполнения запросов к серверу и проверки ответа.

12. Заключение

В этом руководстве мы изучили платформу Takes, создав простое веб-приложение.

Во-первых, мы увидели быстрый способ настроить фреймворк в нашем проекте Maven и запустить наше приложение.

Затем мы рассмотрели несколько общих функций, таких как маршрутизация, обработка запросов/ответов и модульное тестирование. ``

Наконец, мы изучили поддержку модульного и интеграционного тестирования, предоставляемую фреймворком.

Как обычно, все реализации кода доступны на GitHub .