Introduction to Ratpack – 鼠皮袋简介

最后修改: 2017年 3月 16日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Ratpack is a set of JVM based libraries built for modern days high-performance real-time applications. It’s built on top of embedded Netty event-driven networking engine and is fully compliant with the reactive design pattern.

Ratpack是一套基于JVM的库,为现代的高性能实时应用程序而构建。它建立在嵌入式Netty事件驱动的网络引擎之上,并且完全符合反应式设计模式。

In this article, we’ll learn how to use Ratpack and we’ll build a small application using it.

在这篇文章中,我们将学习如何使用Ratpack,并使用它建立一个小程序。

2. Why Ratpack?

2.为什么是Ratpack?

The main advantages of Ratpack:

Ratpack的主要优势。

  • it’s very lightweight, fast and scalable
  • it consumes less memory than other frameworks such as DropWizard; an interesting benchmark comparison result can be found here
  • since it’s built on top of Netty, Ratpack is totally event-driven and non-blocking in nature
  • it has support for Guice dependency management
  • much like Spring Boot, Ratpack has its own testing libraries to quickly setup test-cases

3. Creating an Application

3.创建一个应用程序

To understand how Ratpack works, let’s start by creating a small application with it.

为了了解Ratpack是如何工作的,让我们先用它创建一个小程序。

3.1. Maven Dependencies

3.1.Maven的依赖性

First, let’s add the following dependencies into our pom.xml:

首先,让我们在我们的pom.xml中添加以下依赖项:

<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-core</artifactId>
    <version>1.4.5</version>
</dependency>
<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-test</artifactId>
    <version>1.4.5</version>
</dependency>

You can check the latest version on Maven Central.

您可以在Maven Central上查看最新版本。

Note that although we are using Maven as our build system, as per Ratpack recommendation, it’s better to use Gradle as a build tool since Ratpack has first-class Gradle support provided via the Ratpack’s Gradle plugin.

请注意,尽管我们使用Maven作为构建系统,但根据Ratpack的建议,最好使用Gradle作为构建工具,因为Ratpack通过Ratpack的Gradle插件提供一流的Gradle支持。

We can use the following build Gradle script:

我们可以使用下面的构建Gradle脚本。

buildscript {
    repositories {
      jcenter()
    }
    dependencies {
      classpath "io.ratpack:ratpack-gradle:1.4.5"
    }
}
 
apply plugin: "io.ratpack.ratpack-java"
repositories {
    jcenter()
}
dependencies {
    testCompile 'junit:junit:4.11'
    runtime "org.slf4j:slf4j-simple:1.7.21"
}
test {
    testLogging {
      events 'started', 'passed'
    }
}

3.2. Building the Application

3.2.构建应用程序

Once our build management is configured, we need to create a class to start the embedded Netty server and build a simple context to handle the default requests:

一旦我们的构建管理被配置好,我们需要创建一个类来启动嵌入式Netty服务器,并建立一个简单的上下文来处理默认请求。

public class Application {
	
    public static void main(String[] args) throws Exception {
        RatpackServer.start(server -> server.handlers(chain -> chain
          .get(ctx -> ctx.render("Welcome to Baeldung ratpack!!!"))));
    }
}

As we can see, by using RatpackServer we can now start the server (default port 5050). The handlers() method takes a function that receives a Chain object, which maps all the respective incoming requests. This “Handler Chain API” is used for building the response handling strategy.

我们可以看到,通过使用RatpackServer,我们现在可以启动服务器(默认端口5050)。handlers()方法接收一个函数,该函数接收一个对象,该对象映射所有各自传入的请求。这个 “处理程序链API “被用于构建响应处理策略。

If we run this code snippet and hit the browser at http://localhost:5050, “Welcome to Baeldung ratpack!!!” should be displayed.

如果我们运行这个代码片段,并在http://localhost:5050,”欢迎来到Baeldung ratpack!!!”应该会显示。

Similarly, we can map an HTTP POST request.

同样地,我们可以映射一个HTTP POST请求。

3.3. Handling URL Path Parameters

3.3 处理URL路径参数

In the next example, we need to capture some URL path param in our application. In Ratpack we use PathTokens to capture them:

在下一个例子中,我们需要在我们的应用程序中捕获一些URL路径参数。在Ratpack中,我们使用PathTokens来捕获它们。

RatpackServer.start(server -> server
  .handlers(chain -> chain
  .get(":name", ctx -> ctx.render("Hello " 
  + ctx.getPathTokens().get("name") + " !!!"))));

Here, we’re mapping the name URL param. Whenever a request like http://localhost:5050/John would come, the response will be “Hello John !!!”.

在这里,我们正在映射name URL参数。每当一个类似http://localhost:5050/John的请求到来时,响应将是 “Hello John !!”。

3.4. Request/Response Header Modification With/Without Filter

3.4.有/无过滤器的请求/响应标头修改

Sometimes, we need to modify the inline HTTP response header based on our need. Ratpack has MutableHeaders to customize outgoing responses.

有时,我们需要根据自己的需要修改内联的HTTP响应头。Ratpack有MutableHeaders来定制出站响应。

For example, we need to alter following headers in the response: Access-Control-Allow-Origin, Accept-Language, and Accept-Charset:

例如,我们需要改变响应中的以下头信息。Access-Control-Allow-Origin, Accept-Language, 和Accept-Charset

RatpackServer.start(server -> server.handlers(chain -> chain.all(ctx -> {
    MutableHeaders headers = ctx.getResponse().getHeaders();
    headers.set("Access-Control-Allow-Origin", "*");
    headers.set("Accept-Language", "en-us");
    headers.set("Accept-Charset", "UTF-8");
    ctx.next();
}).get(":name", ctx -> ctx
    .render("Hello " + ctx.getPathTokens().get("name") + "!!!"))));

By using MutableHeaders we set are setting the three headers and pushing them in the Chain.

通过使用MutableHeaders,我们设置了三个头信息,并将它们推送到Chain

In the same way, we can check the incoming request headers too:

以同样的方式,我们也可以检查传入的请求头信息。

ctx.getRequest().getHeaders().get("//TODO")

The same can be achieved by creating a filter. Ratpack has a Handler interface, which can be implemented to create a filter. It has only one method handle(), which takes the current Context as a parameter:

同样可以通过创建一个过滤器来实现。Ratpack有一个Handler接口它可以被实现来创建一个过滤器。它只有一个方法handle(),,将当前Context作为参数。

public class RequestValidatorFilter implements Handler {

    @Override
    public void handle(Context ctx) throws Exception {
        MutableHeaders headers = ctx.getResponse().getHeaders();
        headers.set("Access-Control-Allow-Origin", "*");
        ctx.next();
    }
}

We can use this filter in the following way:

我们可以通过以下方式使用这个过滤器。

RatpackServer.start(
    server -> server.handlers(chain -> chain
      .all(new RequestValidatorFilter())
      .get(ctx -> ctx.render("Welcome to baeldung ratpack!!!"))));
}

3.5. JSON Parser

3.5 JSON解析器

Ratpack internally uses faster-jackson for JSON parsing. We can use Jackson module to parse any object to JSON.

Ratpack内部使用faster-jackson进行JSON解析。我们可以使用Jackson模块来解析任何对象为JSON。

Let’s create a simple POJO class which will be used for parsing:

让我们创建一个简单的POJO类,它将用于解析。

public class Employee {

    private Long id;
    private String title;
    private String name;

    // getters and setters 

}

Here, we have created one simple POJO class named Employee, which has three parameters: id, title, and name. Now we will use this Employee object to convert into JSON and return the same when certain URL is hit:

在这里,我们创建了一个名为Employee的简单POJO类,它有三个参数。id, title, and name。现在我们将使用这个Employee对象来转换为JSON,并在某些URL被点击时返回相同的内容。

List<Employee> employees = new ArrayList<Employee>();
employees.add(new Employee(1L, "Mr", "John Doe"));
employees.add(new Employee(2L, "Mr", "White Snow"));

RatpackServer.start(
    server -> server.handlers(chain -> chain
      .get("data/employees",
      ctx -> ctx.render(Jackson.json(employees)))));

As we can see, we are manually adding two Employee objects into a list and parsing them as JSON using Jackson module. As soon as the /data/employees URL is hit, the JSON object will be returned.

正如我们所看到的,我们正在手动添加两个Employee对象到一个列表中,并使用Jackson模块将它们解析为JSON。一旦/data/employees URL被点击,JSON对象将被返回。

Point to note here is that we are not using ObjectMapper at all since Ratpack’s Jackson module will do the needful on the fly.

这里需要注意的是,我们根本没有使用ObjectMapper,因为Ratpack的Jackson模块会即时完成所需的工作。

3.6. In-Memory Database

3.6.内存数据库

Ratpack has the first class support for in-memory databases. It uses HikariCP for JDBC connection pooling. In order to use it, we need to add Ratpack’s HikariCP module dependency in the pom.xml:

Ratpack对内存数据库有一流的支持。它使用HikariCP进行JDBC连接池。为了使用它,我们需要在pom.xml中添加Ratpack的HikariCP模块依赖项

<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-hikari</artifactId>
    <version>1.4.5</version>
</dependency>

If we are using Gradle, the same needs to be added in the Gradle build file:

如果我们使用Gradle,同样需要在Gradle构建文件中添加。

compile ratpack.dependency('hikari')

Now, we need to create an SQL file with table DDL statements so that the tables are created as soon as the server is up and running. We’ll create the DDL.sql file in the src/main/resources directory and add some DDL statements into it.

现在,我们需要创建一个带有表DDL语句的SQL文件,以便在服务器启动和运行后立即创建表。我们将在src/main/resources目录下创建DDL.sql文件,并在其中添加一些DDL语句。

Since we’re using H2 database, we have to add dependencies for that too.

由于我们使用的是H2数据库,所以我们也必须为其添加依赖性。

Now, by using HikariModule, we can initialize the database at the runtime:

现在,通过使用HikariModule,我们可以在运行时初始化数据库。

RatpackServer.start(
    server -> server.registry(Guice.registry(bindings -> 
      bindings.module(HikariModule.class, config -> {
          config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
          config.addDataSourceProperty("URL",
          "jdbc:h2:mem:baeldung;INIT=RUNSCRIPT FROM 'classpath:/DDL.sql'");
      }))).handlers(...));

4. Testing

4.测试

As mentioned earlier, Ratpack has first-class support for jUnit test cases. By using MainClassApplicationUnderTest we can easily create test cases and test the endpoints:

如前所述,Ratpack对jUnit测试用例有一流的支持。通过使用MainClassApplicationUnderTest,我们可以轻松地创建测试案例并测试端点。

@RunWith(JUnit4.class)
public class ApplicationTest {

    MainClassApplicationUnderTest appUnderTest
      = new MainClassApplicationUnderTest(Application.class);

    @Test
    public void givenDefaultUrl_getStaticText() {
        assertEquals("Welcome to baeldung ratpack!!!", 
          appUnderTest.getHttpClient().getText("/"));
    }

    @Test
    public void givenDynamicUrl_getDynamicText() {
        assertEquals("Hello dummybot!!!", 
          appUnderTest.getHttpClient().getText("/dummybot"));
    }

    @Test
    public void givenUrl_getListOfEmployee() 
      throws JsonProcessingException {
 
        List<Employee> employees = new ArrayList<Employee>();
        ObjectMapper mapper = new ObjectMapper();
        employees.add(new Employee(1L, "Mr", "John Doe"));
        employees.add(new Employee(2L, "Mr", "White Snow"));

        assertEquals(mapper.writeValueAsString(employees), 
          appUnderTest.getHttpClient().getText("/data/employees"));
    }
 
    @After
    public void shutdown() {
        appUnderTest.close();
    }

}

Please note that we need to manually terminate the running MainClassApplicationUnderTest instance by calling the close() method as it may unnecessarily block JVM resources. That’s why we have used @After annotation to forcefully terminate the instance once the test case executed.

请注意,我们需要通过调用close()方法来手动终止正在运行的MainClassApplicationUnderTest实例,因为它可能不必要地阻塞JVM资源。这就是为什么我们使用@After注解来强制终止测试案例执行后的实例。

5. Conclusion

5.结论

In this article, we saw the simplicity of using Ratpack.

在这篇文章中,我们看到了使用Ratpack的简单性。

As always, the full source code is available over on GitHub.

一如既往,完整的源代码可在GitHub上获得