本手册正在开发中,目前尚未完善。
如果您想帮助改进它,我们希望您能做到,请参见README

16 Jackson

Jackson JSON 序列化库 的集成提供了处理 JSON 的能力。这是作为 ratpack-core 的一部分提供的。

从 Ratpack 2.0.0-rc-1 开始,它针对 (并依赖于) Jackson Core 2.13.1 构建。

ratpack.core.jackson.Jackson 类提供了大多数与 Jackson 相关的功能。

1.16 编写 JSON 响应

Jackson 集成添加了一个 Renderer 用于将对象渲染为 JSON。

Jackson.json() 方法可用于包装任何对象 (可由 Jackson 序列化) 以与 Context.render() 方法一起使用。

import ratpack.test.embed.EmbeddedApp;
import ratpack.core.http.client.ReceivedResponse;

import static ratpack.core.jackson.Jackson.json;
import static org.junit.jupiter.api.Assertions.*;

public class Example {

  public static class Person {
    private final String name;
    public Person(String name) {
      this.name = name;
    }
    public String getName() {
      return name;
    }
  }

  public static void main(String... args) throws Exception {
    EmbeddedApp.of(s -> s
      .handlers(chain ->
        chain.get(ctx -> ctx.render(json(new Person("John"))))
      )
    ).test(httpClient -> {
      ReceivedResponse response = httpClient.get();
      assertEquals("{\"name\":\"John\"}", response.getBody().getText());
      assertEquals("application/json", response.getBody().getContentType().getType());
    });
  }
}

有关更多示例(包括流式传输和 JSON 事件),请参阅 Jackson 类文档。

2.16 读取 JSON 请求

Jackson 集成添加了一个 Parser 用于将 JSON 请求主体转换为对象。

Jackson.jsonNode()Jackson.fromJson() 方法可用于创建将与 Context.parse() 方法一起使用的对象。

import ratpack.guice.Guice;
import ratpack.test.embed.EmbeddedApp;
import ratpack.core.http.client.ReceivedResponse;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.reflect.TypeToken;

import java.util.List;

import static ratpack.func.Types.listOf;
import static ratpack.core.jackson.Jackson.jsonNode;
import static ratpack.core.jackson.Jackson.fromJson;
import static org.junit.jupiter.api.Assertions.*;

public class Example {

  public static class Person {
    private final String name;
    public Person(@JsonProperty("name") String name) {
      this.name = name;
    }
    public String getName() {
      return name;
    }
  }

  public static void main(String... args) throws Exception {
    EmbeddedApp.of(s -> s
      .handlers(chain -> chain
        .post("asNode", ctx -> {
          ctx.render(ctx.parse(jsonNode()).map(n -> n.get("name").asText()));
        })
        .post("asPerson", ctx -> {
          ctx.render(ctx.parse(fromJson(Person.class)).map(p -> p.getName()));
        })
        .post("asPersonList", ctx -> {
          ctx.render(ctx.parse(fromJson(listOf(Person.class))).map(p -> p.get(0).getName()));
        })
      )
    ).test(httpClient -> {
      ReceivedResponse response = httpClient.requestSpec(s ->
        s.body(b -> b.type("application/json").text("{\"name\":\"John\"}"))
      ).post("asNode");
      assertEquals("John", response.getBody().getText());

      response = httpClient.requestSpec(s ->
        s.body(b -> b.type("application/json").text("{\"name\":\"John\"}"))
      ).post("asPerson");
      assertEquals("John", response.getBody().getText());

      response = httpClient.requestSpec(s ->
        s.body(b -> b.type("application/json").text("[{\"name\":\"John\"}]"))
      ).post("asPersonList");
      assertEquals("John", response.getBody().getText());
    });
  }
}

该集成添加了一个 no opts 解析器,它使使用 Context.parse(Class)Context.parse(TypeToken) 方法成为可能。

import ratpack.test.embed.EmbeddedApp;
import ratpack.core.http.client.ReceivedResponse;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.reflect.TypeToken;

import java.util.List;

import static ratpack.func.Types.listOf;
import static org.junit.jupiter.api.Assertions.*;

public class Example {

  public static class Person {
    private final String name;
    public Person(@JsonProperty("name") String name) {
      this.name = name;
    }
    public String getName() {
      return name;
    }
  }

  public static void main(String... args) throws Exception {
    EmbeddedApp.of(s -> s
      .handlers(chain -> chain
        .post("asPerson", ctx -> {
          ctx.parse(Person.class).then(person -> ctx.render(person.getName()));
        })
        .post("asPersonList", ctx -> {
          ctx.parse(listOf(Person.class)).then(person -> ctx.render(person.get(0).getName()));
        })
      )
    ).test(httpClient -> {
      ReceivedResponse response = httpClient.requestSpec(s ->
        s.body(b -> b.type("application/json").text("{\"name\":\"John\"}"))
      ).post("asPerson");
      assertEquals("John", response.getBody().getText());

      response = httpClient.requestSpec(s ->
        s.body(b -> b.type("application/json").text("[{\"name\":\"John\"}]"))
      ).post("asPersonList");
      assertEquals("John", response.getBody().getText());
    });
  }
}

3.16 配置 Jackson

Jackson API 基于 ObjectMapper。Ratpack 自动将默认实例添加到基本注册表中。要配置 Jackson 行为,请覆盖此实例。

Jackson 功能模块 允许扩展 Jackson 以支持额外的數據類型和功能。例如 JDK8 模块 添加了对 JDK8 类型(如 Optional)的支持。

要使用此类模块,只需将适当配置的 ObjectMapper 添加到注册表中即可。

import ratpack.test.embed.EmbeddedApp;
import ratpack.core.http.client.ReceivedResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;

import java.util.Optional;

import static ratpack.core.jackson.Jackson.json;
import static org.junit.jupiter.api.Assertions.*;

public class Example {

  public static class Person {
    private final String name;
    public Person(String name) {
      this.name = name;
    }
    public String getName() {
      return name;
    }
  }

  public static void main(String... args) throws Exception {
    EmbeddedApp.of(s -> s
      .registryOf(r -> r
        .add(ObjectMapper.class, new ObjectMapper().registerModule(new Jdk8Module())) 
      )
      .handlers(chain ->
        chain.get(ctx -> {
          Optional<Person> personOptional = Optional.of(new Person("John"));
          ctx.render(json(personOptional));
        })
      )
    ).test(httpClient -> {
      ReceivedResponse response = httpClient.get();
      assertEquals("{\"name\":\"John\"}", response.getBody().getText());
      assertEquals("application/json", response.getBody().getContentType().getType());
    });
  }
}