1. 概述

Ratpack 是一套轻量级的 Java 库,专为构建可扩展的 HTTP 应用程序设计,支持响应式、异步和非阻塞特性。

此外,Ratpack 还支持与 Google Guice、Spring Boot、RxJava 和 Hystrix 等技术或框架集成。

在本教程中,我们将探讨 如何在 Groovy 中使用 Ratpack。通过 Groovy 的动态特性与脚本能力,结合 Ratpack 的简洁 API,可以非常快速地搭建高性能的 Web 应用。

2. 为何选择 Groovy

Groovy 是一种运行在 JVM 上的动态语言,功能强大且语法灵活。

Groovy 让脚本编写和领域特定语言(DSL)的开发变得非常简单。结合 Ratpack,可以实现 Web 应用的快速开发。

Ratpack 提供了对 Groovy 的良好支持,主要通过两个核心库:ratpack-groovyratpack-groovy-test

3. 使用 Groovy 脚本创建 Ratpack 应用

Ratpack 的 Groovy API 是用 Java 编写的,因此可以轻松与 Java 或 Groovy 项目集成。这些 API 位于 ratpack.groovy 包中。

结合 Groovy 的脚本能力和 Grape 依赖管理机制,我们只需几行代码就能快速构建一个基于 Ratpack 的 Web 应用:

@Grab('io.ratpack:ratpack-groovy:1.6.1')
import static ratpack.groovy.Groovy.ratpack

ratpack {
    handlers {
        get {
            render 'Hello World from Ratpack with Groovy!!'
        }    
    }
}

✅ 这是我们第一个处理 GET 请求的 handler。我们只需要添加一些 DSL 语句即可启用 Ratpack 服务。

运行该脚本后,默认服务地址为 http://localhost:5050

$ curl -s localhost:5050
Hello World from Ratpack with Groovy!!

也可以通过 ServerConfig 自定义端口:

ratpack {
    serverConfig {
        port(5056)
    }
}

⚠️ Ratpack 还支持热重载功能,修改 Ratpack.groovy 后,下一次 HTTP 请求即可看到变更。

4. Ratpack-Groovy 的依赖管理

启用 ratpack-groovy 支持有多种方式:

4.1. 使用 Grape

Groovy 内置的依赖管理工具 Grape 可以非常方便地引入依赖:

@Grab('io.ratpack:ratpack-groovy:1.6.1')
import static ratpack.groovy.Groovy.ratpack

4.2. Maven 依赖

使用 Maven 时,只需在 pom.xml 中添加如下依赖:

<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-groovy</artifactId>
    <version>${ratpack.version}</version>
</dependency>

4.3. Gradle 配置

build.gradle 中添加 Ratpack 的 Groovy 插件:

plugins { 
  id 'io.ratpack.ratpack-groovy' version '1.6.1' 
}

5. Groovy 中的 Ratpack Handler

Handler 是处理 Web 请求和响应的核心机制。 请求和响应对象可以通过闭包访问。

我们可以通过 GET、POST 等 HTTP 方法来定义 handler:

handlers { 
    get("greet/:name") { ctx ->
        render "Hello " + ctx.getPathTokens().get("name") + " !!!"
    }
}      

访问 http://localhost:5050/greet/Norman 测试接口:

$ curl -s localhost:5050/greet/Norman
Hello Norman!!!

在 handler 中,ctx 表示上下文对象,用于访问路径变量、请求和响应等信息。

Ratpack 也通过 Jackson 支持 JSON 数据处理。例如,我们可以将 Groovy Map 转换为 JSON 响应:

get("data") {
    render Jackson.json([title: "Mr", name: "Norman", country: "USA"])
}

测试接口:

$ curl -s localhost:5050/data
{"title":"Mr","name":"Norman","country":"USA"}

6. Groovy 中的 Ratpack Promise

Ratpack 的异步和非阻塞特性是通过 Promise 实现的。Promise 类似于 JavaScript 中的 Promise,也可以类比 Java 中的 Future。它代表一个将来可用的值。

示例代码如下:

post("user") {
    Promise<User> user = parse(Jackson.fromJson(User)) 
    user.then { u -> render u.name } 
}

解析说明:

  • Jackson.fromJson(User) 通过 ObjectMapper 解析请求体中的 JSON。
  • Context.parse 将其绑定为 Promise。
  • 异步操作完成后,通过 then 处理结果并返回响应。

测试接口:

curl -X POST -H 'Content-type: application/json' --data \
'{"id":3,"title":"Mrs","name":"Jiney Weiber","country":"UK"}' \
http://localhost:5050/employee

Jiney Weiber

Promise 支持链式操作,如 mapflatMap,可构建复杂的异步逻辑。

7. 与数据库集成

异步 handler 的优势在需要等待外部服务(如数据库)时尤为明显。下面我们演示如何将 Ratpack 与 H2 数据库集成。

7.1. 使用 HikariModule

添加 HikariCP 和 H2 的 Maven 依赖:

<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-hikari</artifactId>
    <version>${ratpack.version}</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>${h2.version}</version>
</dependency>

Gradle 项目可添加如下依赖:

dependencies {
  compile ratpack.dependency('hikari')
  compile "com.h2database:h2:$h2.version"
}

ratpack 配置中声明 HikariModule:

import ratpack.hikari.HikariModule

ratpack {
    bindings {
        module(HikariModule) { config ->
            config.dataSourceClassName = 'org.h2.jdbcx.JdbcDataSource'
            config.addDataSourceProperty('URL', 
              "jdbc:h2:mem:devDB;INIT=RUNSCRIPT FROM 'classpath:/User.sql'")
        }
    }
}

使用 Java 的 ConnectionPreparedStatement 进行数据库操作:

get('fetchUserName/:id') { Context ctx ->
    Connection connection = ctx.get(DataSource.class).getConnection()
    PreparedStatement queryStatement = 
      connection.prepareStatement("SELECT NAME FROM USER WHERE ID=?")
    queryStatement.setInt(1, Integer.parseInt(ctx.getPathTokens().get("id")))
    ResultSet resultSet = queryStatement.executeQuery()
    resultSet.next()
    render resultSet.getString(1)  
}

测试接口:

$ curl -s localhost:5050/fetchUserName/1
Norman Potter

7.2. 使用 Groovy 的 Sql 类

Groovy 提供了便捷的 Sql 类用于快速数据库操作:

get('fetchUsers') {
    def db = [url:'jdbc:h2:mem:devDB']
    def sql = Sql.newInstance(db.url, db.user, db.password)
    def users = sql.rows("SELECT * FROM USER");
    render(Jackson.json(users))
}

测试接口:

$ curl -s localhost:5050/fetchUsers
[{"ID":1,"TITLE":"Mr","NAME":"Norman Potter","COUNTRY":"USA"},
{"ID":2,"TITLE":"Miss","NAME":"Ketty Smith","COUNTRY":"FRANCE"}]

POST 接口示例:

post('addUser') {
    parse(Jackson.fromJson(User))
        .then { u ->
            def db = [url:'jdbc:h2:mem:devDB']
            Sql sql = Sql.newInstance(db.url, db.user, db.password)
            sql.executeInsert("INSERT INTO USER VALUES (?,?,?,?)", 
              [u.id, u.title, u.name, u.country])
            render "User $u.name inserted"
        }
}

测试接口:

$ curl -X POST -H 'Content-type: application/json' --data \
'{"id":3,"title":"Mrs","name":"Jiney Weiber","country":"UK"}' \
http://localhost:5050/addUser

User Jiney Weiber inserted

8. 单元测试

8.1. 测试环境搭建

Ratpack 提供了 ratpack-groovy-test 库用于测试 Groovy 应用。

Maven 项目添加如下依赖:

<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-groovy-test</artifactId>
    <version>1.6.1</version>
</dependency>

Gradle 项目添加:

testCompile ratpack.dependency('groovy-test')

创建主类 RatpackGroovyApp.groovy 用于启动测试服务:

public class RatpackGroovyApp {
    public static void main(String[] args) {
        File file = new File("src/main/groovy/com/baeldung/Ratpack.groovy");
        def shell = new GroovyShell()  
        shell.evaluate(file)
    }
}

编写测试类 RatpackGroovySpec.groovy

class RatpackGroovySpec {
    ServerBackedApplicationUnderTest ratpackGroovyApp = 
      new MainClassApplicationUnderTest(RatpackGroovyApp.class)
    @Delegate TestHttpClient client = 
      TestHttpClient.testHttpClient(ratpackGroovyApp)
}

8.2. 编写测试用例

测试应用是否启动成功:

@Test
void "test if app is started"() {
    when:
    get("")

    then:
    assert response.statusCode == 200
    assert response.body.text == 
      "Hello World from Ratpack with Groovy!!"
}

测试 fetchUsers 接口:

@Test
void "test fetchUsers"() {
    when:
    get("fetchUsers")
        
    then:
    assert response.statusCode == 200
    assert response.body.text == 
      '[{"ID":1,"TITLE":"Mr","NAME":"Norman Potter","COUNTRY":"USA"},{"ID":2,"TITLE":"Miss","NAME":"Ketty Smith","COUNTRY":"FRANCE"}]'
}

Ratpack 的测试框架会自动管理服务器的启动与关闭。

9. 总结

本文介绍了如何在 Groovy 中使用 Ratpack 构建 Web 应用,包括:

  • 使用 Groovy 脚本快速启动服务
  • Handler 的定义与 JSON 响应处理
  • 异步 Promise 的使用
  • 与 H2 数据库的集成(HikariModule 与 Groovy Sql)
  • 使用 Ratpack 提供的测试框架进行单元测试

通过 Groovy 的闭包、DSL 和 Sql 支持,代码更加简洁、高效、可读性高。同时,Groovy 的测试能力也使得集成测试变得简单。

借助这些特性,我们可以快速构建高性能、可扩展的 HTTP 应用。

完整示例代码可在 GitHub 上找到。


原始标题:Ratpack with Groovy