本手册尚未完善,正在不断更新中。
如果您想帮助改进它,我们希望您能够这样做,请参阅 README

6 上下文

Context 类型是 Ratpack 的核心。

它提供了

有关直接使用请求/响应的信息,请参阅 HTTP 章

有关委托的信息,请参阅 处理器章

1.6 上下文对象

上下文是一个 注册表。它提供对通过类型查找在处理器管道中上游提供的对象的访问。这是 Ratpack 中处理器间协作的机制。

考虑以下示例

import ratpack.test.embed.EmbeddedApp;
import ratpack.exec.registry.Registry;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class Example {

  public static interface Person {
    String getId();

    String getStatus();

    String getAge();
  }

  public static class PersonImpl implements Person {
    private final String id;
    private final String status;
    private final String age;

    public PersonImpl(String id, String status, String age) {
      this.id = id;
      this.status = status;
      this.age = age;
    }

    @Override
    public String getId() {
      return id;
    }

    @Override
    public String getStatus() {
      return status;
    }

    @Override
    public String getAge() {
      return age;
    }
  }

  public static void main(String... args) throws Exception {
    EmbeddedApp
      .fromHandlers(chain -> chain
          .prefix("person/:id", (personChain) -> personChain
            .all(ctx -> {
              String id = ctx.getPathTokens().get("id"); // (1)
              Person person = new PersonImpl(id, "example-status", "example-age");
              ctx.next(Registry.single(Person.class, person)); // (2)
            })
            .get("status", ctx -> {
              Person person = ctx.get(Person.class); // (3)
              ctx.render("person " + person.getId() + " status: " + person.getStatus());
            })
            .get("age", ctx -> {
              Person person = ctx.get(Person.class); // (4)
              ctx.render("person " + person.getId() + " age: " + person.getAge());
            }))
      )
      .test(httpClient -> {
        assertEquals("person 10 status: example-status", httpClient.get("person/10/status").getBody().getText());
        assertEquals("person 6 age: example-age", httpClient.get("person/6/age").getBody().getText());
      });
  }
}

(2) 中,我们将 Person 实例推送到注册表中,以便下游处理器使用,并在 (3)(4) 中它们如何检索它。我们将创建细节与使用分离,并避免在 statusage 处理器中重复创建代码。避免重复的好处是显而易见的。稍微细微一点的是,这种分离使得在没有将下游处理器实现为匿名类的情况下更容易进行测试(有关信息,请参阅 测试章)。

(1) 中,我们也使用了上下文对象。 prefix() 链方法绑定在请求路径上,可能会捕获令牌。如果绑定成功,则一个 PathBinding 对象将与描述绑定结果的上下文注册。这包括作为绑定一部分捕获的任何路径令牌。在上面的情况下,我们将第二个路径组件捕获为 id。上下文上的 getPathTokens() 方法实际上是同一上下文的 get(PathBinding.class).getPathTokens() 的简写。这是使用上下文对象机制进行处理器间通信的另一个示例。

使用上下文对象的另一个示例是访问文件系统的文件的简写。考虑以下脚本,它使用上下文的 file 方法从文件系统中检索静态资产


import static ratpack.groovy.Groovy.ratpack ratpack { handlers { get { def f = file('../') render f ?: "null-value" } } }

在上面的示例中,上下文的 file() 方法被调用以检索提供的路径的 java.io.File 实例。上下文的 file() 方法是检索注册表中 FileSystemBinding 对象的简写,实际上是 get(FileSystemBinding.class).file(path/to/file) 的简写。上下文将始终解析相对于应用程序根目录的静态文件资产,因此,在提供绝对路径的情况下,需要注意的是,将使用应用程序存在的路径作为前缀来解析静态文件资产的路径。例如,如果您的应用程序存在于 /home/ratpack/app 中,并且您的处理器使用 file 方法解析 /etc/passwd,那么解析的实际路径将是 /home/ratpack/app/etc/passwd。在无法从应用程序根目录中解析文件的情况下,file() 方法可能会返回一个空值,这在上面的示例中得到了体现。开发人员负责处理访问文件可能返回一个空对象的情况。

1.1.6 分区

上下文对象机制支持通过向不同的分区提供不同的对象来对应用程序逻辑进行分区。这是因为与上下文注册的对象隐式地具有作用域,具体取决于它们的注册方式。使用 next() 方法注册的对象可用于属于同一插入(即 context.insert() 包括嵌套插入)的所有下游处理器。使用 insert() 方法注册的对象可用于插入的处理器及其嵌套插入。

这通常用于对应用程序的不同部分使用不同的错误处理策略。

import ratpack.core.error.ServerErrorHandler;
import ratpack.test.embed.EmbeddedApp;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class Example {

  public static void main(String... args) throws Exception {
    EmbeddedApp.fromHandlers(chain -> chain
        .prefix("api", api -> api
            .register(r -> r.add(ServerErrorHandler.class, (context, throwable) ->
                  context.render("api error: " + throwable.getMessage())
              )
            )
            .all(ctx -> {
              throw new Exception("in api - " + ctx.getRequest().getPath());
            })
        )
        .register(r -> r.add(ServerErrorHandler.class, (ctx, throwable) ->
              ctx.render("app error: " + throwable.getMessage())
          )
        )
        .all(ctx -> {
          throw new Exception("in app - " + ctx.getRequest().getPath());
        })
    ).test(httpClient -> {
      assertEquals("api error: in api - api/foo", httpClient.get("api/foo").getBody().getText());
      assertEquals("app error: in app - bar", httpClient.get("bar").getBody().getText());
    });
  }

}