1.2 Reactive Core
spring-web模块包含以下对反应式Web应用程序的基本支持:
对于服务器请求处理,有两个级别的支持。
- HttpHandler:使用非阻塞I / O和Reactive Streams背压进行HTTP请求处理的基本协定,以及Reactor Netty,Undertow,Tomcat,Jetty以及任何Servlet 3.1+容器的适配器。
- WebHandler API:稍高级别的通用Web API,用于处理请求,在此之上构建了具体的编程模型,例如带注解的控制器和功能端点。
对于客户端,有一个基本的ClientHttpConnector协定,以执行具有非阻塞I / O和响应流反压力的HTTP请求,以及用于Reactor Netty和响应式Jetty HttpClient的适配器。 应用程序中使用的更高级别的WebClient基于此基本协定。
对于客户端和服务器,编解码器用于序列化和反序列化HTTP请求和响应内容。
HttpHandler是具有单个方法的简单协定,用于处理请求和响应。 它故意是最小的,它的主要也是唯一的目的是成为对不同HTTP服务器API的最小抽象。
下表描述了受支持的服务器API:
Server name | Server API used | Reactive Streams support |
---|---|---|
Netty | Netty API | Reactor Netty |
Undertow | Undertow API | spring-web: Undertow to Reactive Streams bridge |
Tomcat | Servlet 3.1 non-blocking I/O; Tomcat API to read and write ByteBuffers vs byte[] | spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge |
Jetty | Servlet 3.1 non-blocking I/O; Jetty API to write ByteBuffers vs byte[] | spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge |
Servlet 3.1 container | Servlet 3.1 non-blocking I/O | spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge |
下表描述了服务器依赖性(另请参阅受支持的版本):
Server name | Group id | Artifact name |
---|---|---|
Reactor Netty | io.projectreactor.netty | reactor-netty |
Undertow | io.undertow | undertow-core |
Tomcat | org.apache.tomcat.embed | tomcat-embed-core |
下面的代码段显示了如何将HttpHandler适配器与每个服务器API一起使用:
Reactor Netty
HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create(host, port).newHandler(adapter).block();
Undertow
HttpHandler handler = ...
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();
Tomcat
HttpHandler handler = ...
Servlet servlet = new TomcatHttpHandlerAdapter(handler);
Tomcat server = new Tomcat();
File base = new File(System.getProperty("java.io.tmpdir"));
Context rootContext = server.addContext("", base.getAbsolutePath());
Tomcat.addServlet(rootContext, "main", servlet);
rootContext.addServletMappingDecoded("/", "main");
server.setHost(host);
server.setPort(port);
server.start();
Jetty
HttpHandler handler = ...
Servlet servlet = new JettyHttpHandlerAdapter(handler);
Server server = new Server();
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
contextHandler.addServlet(new ServletHolder(servlet), "/");
contextHandler.start();
ServerConnector connector = new ServerConnector(server);
connector.setHost(host);
connector.setPort(port);
server.addConnector(connector);
server.start();
Servlet 3.1+ Container
要将其作为WAR部署到任何Servlet 3.1+容器,您可以扩展WAR并将其包括在AbstractReactiveWebInitializer中。 该类使用ServletHttpHandlerAdapter包装HttpHandler并将其注册为Servlet。
org.springframework.web.server包基于HttpHandler契约构建,以提供通用的Web API,以通过多个WebExceptionHandler,多个WebFilter和单个WebHandler组件的链来处理请求。 通过简单地指向自动检测组件的Spring ApplicationContext和/或通过向构建器注册组件,可以将该链与WebHttpHandlerBuilder放在一起。
尽管HttpHandler的目标很简单,即抽象化不同HTTP服务器的使用,但WebHandler API的目的是提供Web应用程序中常用的更广泛的功能集,例如:
- 具有属性的用户会话。
- 请求属性。
- 请求的解析的语言环境或主体。
- 访问已解析和缓存的表单数据。
- 多部分数据的抽象。
Special bean types
下表列出了WebHttpHandlerBuilder可以在Spring ApplicationContext中自动检测的组件,或者可以直接向其注册的组件:
Bean name | Bean type | Count | Description |
---|---|---|---|
<any> | WebExceptionHandler | 0..N | 提供对WebFilter实例链和目标 链中的异常的处理WebHandler。有关更多详细信息,请参见Exceptions。 |
<any> | WebFilter | 0..N | 在其余过滤链和目标之前和之后应用拦截式逻辑WebHandler。有关更多详细信息,请参见过滤器。 |
webHandler | WebHandler | 1 | 请求的处理程序。 |
webSessionManager | WebSessionManager | 0..1 | WebSession通过上的方法公开的实例的管理器ServerWebExchange。 DefaultWebSessionManager默认。 |
serverCodecConfigurer | ServerCodecConfigurer | 0..1 | 用于访问HttpMessageReader实例以解析表单数据和多部分数据,然后通过上的方法公开这些数据ServerWebExchange。ServerCodecConfigurer.create()默认 |
localeContextResolver | LocaleContextResolver | 0..1 | LocaleContext通过在上的方法公开的解析器ServerWebExchange。 AcceptHeaderLocaleContextResolver默认。 |
forwardedHeaderTransformer | ForwardedHeaderTransformer | 0..1 | 对于处理转发的类型标头,可以通过提取和删除它们或仅通过删除它们来进行。默认不使用。 |
表格数据
ServerWebExchange 公开以下访问表单数据的方法:
Mono<MultiValueMap<String, String>> getFormData();
DefaultServerWebExchange使用配置的HttpMessageReader将表单数据(application / x-www-form-urlencoded)解析为MultiValueMap。 默认情况下,FormHttpMessageReader配置为由ServerCodecConfigurer Bean使用(请参阅Web Handler API)。
Multipart Data
ServerWebExchange公开以下访问多部分数据的方法:
Mono <MultiValueMap <String,Part >> getMultipartData();
DefaultServerWebExchange使用配置的HttpMessageReader <MultiValueMap <String,Part >>将multipart / form-data内容解析为MultiValueMap。 当前,Synchronoss NIO Multipart是唯一受支持的第三方库,并且是我们知道的用于非阻塞解析多部分请求的唯一库。 通过ServerCodecConfigurer bean启用它(请参阅Web Handler API)。
要以流方式解析多部分数据,可以使用从HttpMessageReader <Part>返回的Flux <Part>。 例如,在带注解的控制器中,使用@RequestPart意味着按名称对各个部分进行类似于Map的访问,因此需要完全解析多部分数据。 相反,您可以使用@RequestBody将内容解码为Flux <Part>而不收集到MultiValueMap。
Forwarded Headers
当请求通过代理(例如负载平衡器)进行处理时,主机,端口和方案可能会更改,这使得从客户端角度创建指向正确的主机,端口和方案的链接带来了挑战。
RFC 7239定义了代理可以用来提供有关原始请求的信息的HTTP转发头。还有其他非标准标头,包括X-Forwarded-Host,X-Forwarded-Port,X-Forwarded-Proto,X-Forwarded-Ssl和X-Forwarded-Prefix。
ForwardedHeaderFilter是一个Servlet过滤器,它根据转发的标头修改请求的主机,端口和方案,然后删除这些标头。
对于转发的标头,存在安全方面的考虑,因为应用程序无法知道标头是由代理添加的,还是由恶意客户端添加的。这就是为什么应配置信任边界处的代理以删除来自外部的不受信任的转发标头的原因。您还可以使用removeOnly = true配置ForwardedHeaderFilter,在这种情况下,它将删除但不使用标头。
在5.1版本中,ForwardedHeaderFilter被ForwardedHeaderTransformer弃用并取代,因此可以在创建交换之前更早地处理转发的标头。 如果仍然配置了过滤器,则将其从过滤器列表中删除,而改用ForwardedHeaderTransformer。
在WebHandler API中,可以使用WebFilter在其余过滤器和目标WebHandler的其余处理链之前和之后应用拦截样式的逻辑。 使用WebFlux Config时,注册WebFilter就像将其声明为Spring bean一样简单,并且(可选)通过在bean声明上使用@Order或实现Ordered来表达优先级。
CORS
Spring WebFlux通过控制器上的注解为CORS配置提供了细粒度的支持。 但是,当您将其与Spring Security结合使用时,我们建议您依赖内置的CorsFilter,该产品必须在Spring Security的过滤器链之前订购。
有关更多详细信息,请参见有关CORS和CORS WebFilter的部分。
在WebHandler API中,可以使用WebExceptionHandler来处理WebFilter实例链和目标WebHandler链中的异常。 使用WebFlux Config时,注册WebExceptionHandler就像将其声明为Spring bean一样简单,并且(可选)通过在bean声明上使用@Order或实现Ordered来表达优先级。
下表描述了可用的WebExceptionHandler实现:
Exception Handler | Description |
---|---|
ResponseStatusExceptionHandler | 通过将响应设置为异常的HTTP状态代码,提供对ResponseStatusException类型的异常的处理。 |
WebFluxResponseStatusExceptionHandler | ResponseStatusExceptionHandler的扩展,它也可以确定任何异常时@ResponseStatus批注的HTTP状态代码。该处理程序在WebFlux Config中声明。 |
spring-web和spring-core模块提供支持,通过具有Reactive Streams背压的非阻塞I / O,可以将字节内容与更高级别的对象之间的字节序列进行序列化和反序列化。以下介绍了此支持:
- 编码器和解码器是底层协议,用于独立于HTTP编码和解码内容。
- HttpMessageReader和HttpMessageWriter是对HTTP消息内容进行编码和解码的协定。
- 可以使用EncoderHttpMessageWriter来包装Encoder,以使其适合在Web应用程序中使用,而可以使用DecoderHttpMessageReader来包装Decoder。
- DataBuffer抽象了不同的字节缓冲区表示形式(例如Netty ByteBuf,java.nio.ByteBuffer等),并且是所有编解码器都在处理的内容。有关此主题的更多信息,请参见“ Spring核心”部分中的数据缓冲区和编解码器。
spring-core模块提供byte [],ByteBuffer,DataBuffer,Resource和String编码器和解码器实现。 spring-web模块提供了Jackson JSON,Jackson Smile,JAXB2,Protocol Buffers和其他编码器和解码器,以及仅Web的HTTP消息读取器和写入器实现,用于表单数据,多部分内容,服务器发送的事件等。
ClientCodecConfigurer和ServerCodecConfigurer通常用于配置和自定义要在应用程序中使用的编解码器。请参阅有关配置HTTP消息编解码器的部分。
Jackson JSON
存在Jackson库时,都支持JSON和二进制JSON(Smile)。
Jackson2Decoder的工作方式如下:
- Jackson的异步,非阻塞解析器用于将字节块流聚合到TokenBuffer的每个块中,每个代表JSON对象。
- 每个TokenBuffer都传递给Jackson的ObjectMapper以创建更高级别的对象。
- 解码为单值发布者(例如Mono)时,有一个TokenBuffer。
- 当解码为多值发布者(例如Flux)时,一旦为一个完整的对象接收到足够的字节,每个TokenBuffer就会传递给ObjectMapper。输入内容可以是JSON数组,如果内容类型为“ application / stream + json”,则可以是行分隔的JSON。
Jackson2Encoder的工作方式如下:
- 对于单个值发布者(例如Mono),只需通过ObjectMapper对其进行序列化即可。
- 对于具有“ application / json”的多值发布者,默认情况下使用Flux#collectToList()收集值,然后序列化结果集合。