Ratpack API构建在Java 8上,提供Gradle支持,也可使用任何基于JVM的构建工具。
包是唯一必需的类库:ratpack-core
可选类库:ratpack-groovy、ratpack-guice、ratpack-jackson、ratpack-test
示例import ratpack.test.embed.EmbeddedApp;
import static org.junit.Assert.assertEquals;
public class Example {
public static void main(String... args) throws Exception {
EmbeddedApp.fromHandler(ctx ->
ctx.render("Hello World!")
).test(httpClient ->
assertEquals("Hello World!", httpClient.getText())
);
}
}
服务器(RatpackServer)
RatpackServer用于启动应用。import ratpack.server.RatpackServer;
import ratpack.server.ServerConfig;
import java.net.URI;
public class Main {
public static void main(String... args) throws Exception {
RatpackServer.start(server -> server
.serverConfig(ServerConfig.embedded().publicAddress(new URI("http://cs.xieyonghui.com")))
.registryOf(registry -> registry.add("World!"))
.handlers(chain -> chain
.get(ctx -> ctx.render("Hello " + ctx.get(String.class)))
.get(":name", ctx -> ctx.render("Hello " + ctx.getPathTokens().get("name") + "!"))
)
);
}
}
定义了两个接口:of()、start(),方法接收一个RatpackServerSpec对象,用于配置Ratpack应用的三个基本面:
服务器配置/server config、基础注册表/base registry、根处理程序/root handler。
服务器配置
ServerConfig定义是必需的,用于设置服务器启动项,使用ServerConfig静态方法创建实例。
基础目录
Ratpack服务器配置的一个重点是基础目录。
基础目录实际上是应用程序文件系统的根,便于移植文件系统。
运行时期间根据基础目录,解析应用程序中所有相对路径,如:静态资源(图像、脚本)的相对路径。
baseDir(Path)方法能设置基础目录,但BaseDir.find()更常见,支持在类路径上查找基础目录,提供更好的跨平台移植性。
此方法会在类路径上的“/.ratpack”目录下搜索资源。import ratpack.server.ServerConfig;
import ratpack.test.embed.EphemeralBaseDir;
import ratpack.test.embed.EmbeddedApp;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import static org.junit.Assert.assertEquals;
public class Example {
public static void main(String... args) throws Exception {
EphemeralBaseDir.tmpDir().use(baseDir -> {
baseDir.write("mydir/.ratpack", "");
baseDir.write("mydir/assets/message.txt", "Hello Ratpack!");
Path mydir = baseDir.getRoot().resolve("mydir");
ClassLoader classLoader = new URLClassLoader(new URL[]{mydir.toUri().toURL()});
Thread.currentThread().setContextClassLoader(classLoader);
EmbeddedApp.of(serverSpec -> serverSpec
.serverConfig(c -> c.baseDir(mydir))
.handlers(chain ->
chain.files(f -> f.dir("assets"))
)
).test(httpClient -> {
String message = httpClient.getText("message.txt");
assertEquals("Hello Ratpack!", message);
});
});
}
}
示例中使用EphemeralBaseDir构造新的类加载器上下文,实际应用时主方法只需调用BaseDir.find(),使用适当的类路径启动Ratpack应用程序。
端口
ServerConfigBuilder port(int port) 设置服务器的端口。默认端口5050。
Ratpack Registry
一个registry是一个按类型存储的对象,应用程序中可能有许多不同的注册表。import ratpack.handling.Handler;
import ratpack.handling.Context;
import ratpack.registry.Registry;
import static org.junit.Assert.assertTrue;
public class Thing {
private final String name
public Thing(String name) { this.name = name; }
public String getName() { return name; }
}
public class UpstreamHandler implements Handler {
public void handle(Context context) {
context.next(Registry.single(new Thing("foo")));
}
}
public class DownstreamHandler implements Handler {
public void handle(Context context) {
assertTrue(context instanceof Registry);
Thing thing = context.get(Thing.class);
context.render(thing.getName());
}
}
import ratpack.test.handling.HandlingResult;
import ratpack.test.handling.RequestFixture;
import static ratpack.handling.Handlers.chain;
import static ratpack.func.Action.noop;
import static org.junit.Assert.assertEquals;
Handler chain = chain(new UpstreamHandler(), new DownstreamHandler());
HandlingResult result = RequestFixture.handle(chain, noop());
assertEquals("foo", result.rendered(String.class));
处理程序
Ratpack Handler是HTTP请求的处理程序,处理程序是可组合的,很少有应用程序只包含一个处理程序。
大多数应用服务器都是复合处理程序,常用handlers(Action)方法创建,使用Chain DSL创建复合处理程序。
启动/停止
应用程序启动时会通知服务器注册表中的所有服务。
应用程序停止时将通知所有服务执行清理程序。import ratpack.server.RatpackServer;
import ratpack.server.ServerConfig;
import ratpack.service.Service;
import ratpack.service.StartEvent;
import ratpack.service.StopEvent;
import java.util.List;
import java.util.LinkedList;
import static org.junit.Assert.*;
public class Example {
static class RecordingService implements Service {
public final List events = new LinkedList<>();
public void onStart(StartEvent event) {
events.add("start");
}
public void onStop(StopEvent event) {
events.add("stop");
}
}
public static void main(String... args) throws Exception {
RecordingService service = new RecordingService();
RatpackServer server = RatpackServer.of(s -> s
.serverConfig(ServerConfig.embedded())
.registryOf(r -> r.add(service))
.handler(r -> ctx -> ctx.render("ok"))
);
assertEquals("[]", service.events.toString());
server.start();
assertEquals("[start]", service.events.toString());
server.reload();
assertEquals("[start, stop, start]", service.events.toString());
server.stop();
assertEquals("[start, stop, start, stop]", service.events.toString());
}
}
单元测试
ExecHarness测试程序是Ratpack测试支持的一部分。
常用Ratpack Promise异步结构代码执行单元测试。
Ratpack Promise用来避免异步调用时的地狱回调。import com.google.common.io.Files;
import ratpack.test.exec.ExecHarness;
import ratpack.exec.Blocking;
import java.io.File;
import java.nio.charset.StandardCharsets;
import static org.junit.Assert.assertEquals;
public class Example {
public static void main(String... args) throws Exception {
File tmpFile = File.createTempFile("ratpack", "test");
Files.write("Hello World!", tmpFile, StandardCharsets.UTF_8);
tmpFile.deleteOnExit();
String content = ExecHarness.yieldSingle(e ->
Blocking.get(() -> Files.toString(tmpFile, StandardCharsets.UTF_8))
).getValueOrThrow();
assertEquals("Hello World!", content);
}
}
目标和理念
目标
快速、高效、可扩展,允许应用在不妥协的前提下在进行复杂性进化,利用非阻塞编程降低成本。
在集成其他工具和库时的灵活性,允许应用程序轻松、彻底地进行测试。
不可及的方面
成为一个完全的“全栈”解决方案;在一个简洁的框架中提供所有的功能;为“业务逻辑”而生的框架。