ServerRequest 和 ServerResponse 是不可变的接口,它们为 JDK 8 提供了对 HTTP 请求和响应的友好访问,包括头信息、正文、方法和状态代码。
ServerRequest
ServerRequest 提供了对 HTTP 方法、URI、头信息和查询参数的访问,而对正文的访问则通过 body 方法提供。
下面的例子将请求正文提取为一个字符串:
String string = request.body(String.class);
下面的例子将主体提取为 List<Person>
,其中 Person 对象是从序列化的形式(如 JSON 或 XML)解码的:
List<Person> people = request.body(new ParameterizedTypeReference<List<Person>>() {});
下面的例子显示了如何访问参数:
MultiValueMap<String, String> params = request.params();
ServerResponse
ServerResponse 提供了对 HTTP 响应的访问,由于它是不可变的,你可以使用构建方法来创建它。你可以使用构建器来设置响应状态,添加响应头,或提供一个主体。下面的例子创建了一个带有 JSON 内容的 200(OK)响应:
Person person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);
下面的例子显示了如何建立一个带有 Location 头和无正文的 201(CREATED)响应:
URI location = ...
ServerResponse.created(location).build();
你也可以使用一个异步的结果作为主体,形式为 CompletableFuture、Publisher 或 ReactiveAdapterRegistry 支持的任何其他类型。比如说:
Mono<Person> person = webClient.get().retrieve().bodyToMono(Person.class);
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);
如果不仅仅是主体,还有状态或头信息都是基于异步类型,你可以使用 ServerResponse 上的静态异步方法,它接受CompletableFuture<ServerResponse>
、Publisher<ServerResponse>
或 ReactiveAdapterRegistry
支持的任何其他异步类型。比如说:
Mono<ServerResponse> asyncResponse = webClient.get().retrieve().bodyToMono(Person.class)
.map(p -> ServerResponse.ok().header("Name", p.name()).body(p));
ServerResponse.async(asyncResponse);
服务器发送的事件可以通过 ServerResponse 的静态 sse 方法提供。该方法提供的构建器允许你发送字符串,或其他对象作为 JSON。比如说:
public RouterFunction<ServerResponse> sse() {
return route(GET("/sse"), request -> ServerResponse.sse(sseBuilder -> {
// 在某处保存 sseBuilder 对象。
}));
}
// 在其他一些线程中,发送一个字符串
sseBuilder.send("Hello world");
// 或者一个对象,它将被转化为 JSON
Person person = ...
sseBuilder.send(person);
// 通过使用其他方法来定制该事件
sseBuilder.id("42")
.event("sse event")
.data(person);
// 并在某一时刻完成
sseBuilder.complete();
Handler Classes / 处理程序类
我们可以把处理程序函数写成一个 lambda,如下例所示:
HandlerFunction<ServerResponse> helloWorld =
request -> ServerResponse.ok().body("Hello World");
这很方便,但在一个应用程序中,我们需要多个函数,而多个内联的 lambda 会变得很混乱。因此,将相关的处理函数组合到一个处理类中是很有用的,它的作用类似于基于注解的应用程序中的 @Controller
。例如,下面这个类暴露了一个反应式的人名库:
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
public class PersonHandler {
private final PersonRepository repository;
public PersonHandler(PersonRepository repository) {
this.repository = repository;
}
// 处理函数,从数据库走概率查询所有的 person 对象,并以 json 格式响应
public ServerResponse listPeople(ServerRequest request) {
List<Person> people = repository.allPeople();
return ok().contentType(APPLICATION_JSON).body(people);
}
public ServerResponse createPerson(ServerRequest request) throws Exception {
Person person = request.body(Person.class);
repository.savePerson(person);
return ok().build();
}
// getPerson 是一个处理函数,返回一个单一的 Person,由 id 路径变量识别。
// 我们从资源库中检索该 Person,并创建一个 JSON 响应,如果它被找到
// 如果没有找到,我们返回一个 404 Not Found 响应。
public ServerResponse getPerson(ServerRequest request) {
int personId = Integer.parseInt(request.pathVariable("id"));
Person person = repository.getPerson(personId);
if (person != null) {
return ok().contentType(APPLICATION_JSON).body(person);
}
else {
return ServerResponse.notFound().build();
}
}
}
Validation / 校验
一个功能端点可以使用 Spring 的验证设施 来对请求体进行验证。例如,给定一个自定义的 Spring 验证器的实现,用于 Person.Case 的验证:
public class PersonHandler {
// 创建验证器实例
private final Validator validator = new PersonValidator();
// ...
public ServerResponse createPerson(ServerRequest request) {
Person person = request.body(Person.class);
validate(person); // 验证参数
repository.savePerson(person);
return ok().build();
}
private void validate(Person person) {
Errors errors = new BeanPropertyBindingResult(person, "person");
validator.validate(person, errors);
// 如果出现错误,这引发异常: 400 状态的响应
if (errors.hasErrors()) {
throw new ServerWebInputException(errors.toString());
}
}
}
处理程序也可以通过创建和注入一个基于 LocalValidatorFactoryBean 的全局 Validator 实例来使用标准的 bean 验证API(JSR-303)。参见 Spring 验证。