HandlerFunction

ServerRequest 和 ServerResponse 是不可变的接口,它们为 JDK 8 提供了对 HTTP 请求和响应的友好访问,包括头信息、正文、方法和状态代码。

ServerRequest

ServerRequest 提供了对 HTTP 方法、URI、头信息和查询参数的访问,而对正文的访问则通过 body 方法提供。

下面的例子将请求正文提取为一个字符串:

  1. String string = request.body(String.class);

下面的例子将主体提取为 List<Person>,其中 Person 对象是从序列化的形式(如 JSON 或 XML)解码的:

  1. List<Person> people = request.body(new ParameterizedTypeReference<List<Person>>() {});

下面的例子显示了如何访问参数:

  1. MultiValueMap<String, String> params = request.params();

ServerResponse

ServerResponse 提供了对 HTTP 响应的访问,由于它是不可变的,你可以使用构建方法来创建它。你可以使用构建器来设置响应状态,添加响应头,或提供一个主体。下面的例子创建了一个带有 JSON 内容的 200(OK)响应:

  1. Person person = ...
  2. ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);

下面的例子显示了如何建立一个带有 Location 头和无正文的 201(CREATED)响应:

  1. URI location = ...
  2. ServerResponse.created(location).build();

你也可以使用一个异步的结果作为主体,形式为 CompletableFuture、Publisher 或 ReactiveAdapterRegistry 支持的任何其他类型。比如说:

  1. Mono<Person> person = webClient.get().retrieve().bodyToMono(Person.class);
  2. ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);

如果不仅仅是主体,还有状态或头信息都是基于异步类型,你可以使用 ServerResponse 上的静态异步方法,它接受CompletableFuture<ServerResponse>Publisher<ServerResponse>ReactiveAdapterRegistry支持的任何其他异步类型。比如说:

  1. Mono<ServerResponse> asyncResponse = webClient.get().retrieve().bodyToMono(Person.class)
  2. .map(p -> ServerResponse.ok().header("Name", p.name()).body(p));
  3. ServerResponse.async(asyncResponse);

服务器发送的事件可以通过 ServerResponse 的静态 sse 方法提供。该方法提供的构建器允许你发送字符串,或其他对象作为 JSON。比如说:

  1. public RouterFunction<ServerResponse> sse() {
  2. return route(GET("/sse"), request -> ServerResponse.sse(sseBuilder -> {
  3. // 在某处保存 sseBuilder 对象。
  4. }));
  5. }
  6. // 在其他一些线程中,发送一个字符串
  7. sseBuilder.send("Hello world");
  8. // 或者一个对象,它将被转化为 JSON
  9. Person person = ...
  10. sseBuilder.send(person);
  11. // 通过使用其他方法来定制该事件
  12. sseBuilder.id("42")
  13. .event("sse event")
  14. .data(person);
  15. // 并在某一时刻完成
  16. sseBuilder.complete();

Handler Classes / 处理程序类

我们可以把处理程序函数写成一个 lambda,如下例所示:

  1. HandlerFunction<ServerResponse> helloWorld =
  2. request -> ServerResponse.ok().body("Hello World");

这很方便,但在一个应用程序中,我们需要多个函数,而多个内联的 lambda 会变得很混乱。因此,将相关的处理函数组合到一个处理类中是很有用的,它的作用类似于基于注解的应用程序中的 @Controller。例如,下面这个类暴露了一个反应式的人名库:

  1. import static org.springframework.http.MediaType.APPLICATION_JSON;
  2. import static org.springframework.web.reactive.function.server.ServerResponse.ok;
  3. public class PersonHandler {
  4. private final PersonRepository repository;
  5. public PersonHandler(PersonRepository repository) {
  6. this.repository = repository;
  7. }
  8. // 处理函数,从数据库走概率查询所有的 person 对象,并以 json 格式响应
  9. public ServerResponse listPeople(ServerRequest request) {
  10. List<Person> people = repository.allPeople();
  11. return ok().contentType(APPLICATION_JSON).body(people);
  12. }
  13. public ServerResponse createPerson(ServerRequest request) throws Exception {
  14. Person person = request.body(Person.class);
  15. repository.savePerson(person);
  16. return ok().build();
  17. }
  18. // getPerson 是一个处理函数,返回一个单一的 Person,由 id 路径变量识别。
  19. // 我们从资源库中检索该 Person,并创建一个 JSON 响应,如果它被找到
  20. // 如果没有找到,我们返回一个 404 Not Found 响应。
  21. public ServerResponse getPerson(ServerRequest request) {
  22. int personId = Integer.parseInt(request.pathVariable("id"));
  23. Person person = repository.getPerson(personId);
  24. if (person != null) {
  25. return ok().contentType(APPLICATION_JSON).body(person);
  26. }
  27. else {
  28. return ServerResponse.notFound().build();
  29. }
  30. }
  31. }

Validation / 校验

一个功能端点可以使用 Spring 的验证设施 来对请求体进行验证。例如,给定一个自定义的 Spring 验证器的实现,用于 Person.Case 的验证:

  1. public class PersonHandler {
  2. // 创建验证器实例
  3. private final Validator validator = new PersonValidator();
  4. // ...
  5. public ServerResponse createPerson(ServerRequest request) {
  6. Person person = request.body(Person.class);
  7. validate(person); // 验证参数
  8. repository.savePerson(person);
  9. return ok().build();
  10. }
  11. private void validate(Person person) {
  12. Errors errors = new BeanPropertyBindingResult(person, "person");
  13. validator.validate(person, errors);
  14. // 如果出现错误,这引发异常: 400 状态的响应
  15. if (errors.hasErrors()) {
  16. throw new ServerWebInputException(errors.toString());
  17. }
  18. }
  19. }

处理程序也可以通过创建和注入一个基于 LocalValidatorFactoryBean 的全局 Validator 实例来使用标准的 bean 验证API(JSR-303)。参见 Spring 验证