Spring Boot WebFlux 2.1.7 中文翻译文档
1. 前言
从一开始学习 netty 到 rxjava、rector,再到 java8 的 completablefuture,就深深的为响应式编程着迷,这种区别于传统的顺序式编程,没准未来能在编程世界开辟一片天地呢!
然后接触到了 webflux 框架,也是充满了浓厚的兴趣,想好好琢磨一番,奈何中文资料实在太少,就打起了英文文档的主意,可惜英文水平实在捉急,总是看下一句,忘了上一句。诶,要不咱一句句翻译出来吧,这样读起来就通顺了,顺便可以造福下后来学习者(想着翻译的东西要被人看,也是一份坚持的动力)。
翻译并没有逐字逐句去纠结,力求语义通顺,有理解错误的地方,还麻烦大家指出,一起学习探讨。另外,文中还补充了一些自己练习的 demo。
原文链接:https://docs.spring.io/spring-boot/docs/2.1.7.release/reference/htmlsingle/#boot-features-webflux
github 练习 demo:https://github.com/jmcuixy/webflux
tips:翻译是一项提高英语和学习技能一举两得的事呀!
2. webflux 简介
spring webflux 是 spring 5.0 引入的新的响应式框架,区别于 spring mvc,它不需要依赖servlet api,它是完全异步非阻塞的,并且基于 reactor 来实现响应式流规范。
spring webflux 有两种表现形式:基于配置和基于注释。基于注释的实现方式非常类似于 springmvc 模型,如以下实例:
@restcontroller @requestmapping("/users") public class myrestcontroller { @getmapping("/\{user}") public mono<user> getuser(@pathvariable long user) { // ... } @getmapping("/\{user}/customers") public flux<customer> getusercustomers(@pathvariable long user) { // ... } @deletemapping("/\{user}") public mono<user> deleteuser(@pathvariable long user) { // ... } }
基于配置的实现方式,把路由和具体请求逻辑分离开,如以下实例:
@configuration public class routingconfiguration { @bean public routerfunction<serverresponse> monorouterfunction(userhandler userhandler) { return route(get("/\{user}").and(accept(application_json)), userhandler::getuser) .androute(get("/\{user}/customers").and(accept(application_json)), userhandler::getusercustomers) .androute(delete("/\{user}").and(accept(application_json)), userhandler::deleteuser); } } @component public class userhandler { public mono<serverresponse> getuser(serverrequest request) { // ... } public mono<serverresponse> getusercustomers(serverrequest request) { // ... } public mono<serverresponse> deleteuser(serverrequest request) { // ... } }
webflux 是 spring 框架的一部分,其参考文档中提供了详细信息。
你可以定义任意数量的 routerfunction bean,以对你的路由进行归纳整理。当然,你也可以针对多个 routerfunction 设置优先级(@order 注解)。
开始一个 webflux 项目,首先,需要将 spring-boot-starter-webflux 模块引入你的项目。值得注意的是,如果你同时引入了 spring-boot-starter-web 和 spring-boot-starter-webflux 模块会导致 spring boot 自动配置spring mvc,而不是 webflux。因为许多 spring 开发人员引入 spring-boot-starter-webflux ,仅仅是为了使用它的响应式编程(这个理由也是绝了),当然你也可以强制把你的项目配置成 webflux:
springapplication.setwebapplicationtype(webapplicationtype.reactive)
3. 自动配置
spring boot 为 spring webflux 提供的自动配置基本能适用于大多数应用。
spring boot 的提供的自动配置主要做了以下两个工作:
- 为 httpmessagereader 和 httpmessagewriter 实例配置 http 编解码器
- 支持服务静态资源映射,包括对 webjars 资源的支持
如果你想要保持 spring boot webflux 的自动配置功能,并且想添加额外的 webflux 配置项,你可以自定义 @configuration 配置类,但不要添加 @enablewebflux 注解。
如果你想要完全控制 webflux,你可以定义@configuration 配置类,并且添加 @enablewebflux. 注解。
4. httpmessagereaders 和 httpmessagewriters 的 http 编解码器
spring webflux 使用 httpmessagereader 和 httpmessagewriter 接口来转换 http 请求和响应,可以通过 codecconfigurer 得到它们的默认配置:
public interface codecconfigurer { ... list<httpmessagereader<?>> getreaders(); list<httpmessagewriter<?>> getwriters(); ... }
spring boot 提供了 codeccustomizer 接口,允许你进一步定制编解码器,通过其 customize() 方法可以获取到 codecconfigurer 对象,从而可以注册新的编解码工具,或对现有的编解码工具进行替换等。如以下实例:
import org.springframework.boot.web.codec.codeccustomizer; @configuration public class myconfiguration { @bean public codeccustomizer mycodeccustomizer() { return codecconfigurer -> { // ... } } }
5. 静态资源
spring boot 默认从类路径的以下目录(/static、 /public 、/resources 、/meta-inf/resources)加载静态资源,当然,你可以自定义配置类实现 webfluxconfigurer 并重写 addresourcehandlers 方法来修改默认资源路径:
@configuration public class mywebfluxconfigurer implements webfluxconfigurer { @override public void addresourcehandlers(resourcehandlerregistry registry) { // do more } }
spring boot 默认将静态资源映射在 /** 的路径下,当然,你可以通过修改 spring.webflux.static-path-pattern 属性来调整默认映射,例如,将所有资源映射到 /resources/** 路径 ,可以通过以下方式实现:
spring.webflux.static-path-pattern=/resources/**
你也可以通过设置 spring.resources.static-locations 属性值来自定义资源目录,如果你这样做了,默认的欢迎页面检测也将会切换到你设置的资源目录。因此,在你的资源目录中,只要有一个 index.html 页面,都将会成为你的应用主页。
除了前面介绍的标准静态资源外,还有一种特殊的情况,那就是 webjars 内容。如果静态资源被打包成了 webjars 的格式,那么访问这些资源的路径就变成了 /webjars/** 。
tips:spring webflux 应用程序不严格依赖 servlet api,因此不能将它们部署为 war 文件,也不使用 src/main/webapp 目录。
6. 模板引擎
spring webflux 除了提供 rest web 服务外,还支持渲染动态 html 内容,spring webflux 支持一系列模板引擎,包括 thymeleaf、freemarker 和 mustache。
spring boot 为以下的模板引擎提供了自动配置的支持:
当你使用了其中某个模板引擎,并选择了 spring boot 自动配置,你需要将你的模板文件放在 src/main/resources/templates 目录下,以便被 spring boot 发现。
7. 异常处理
spring boot 提供了一个 webexceptionhandler 用来处理所有错误,webexceptionhandler 执行通常被认为是处理链中的最后一步,仅位于 webflux 提供服务之前。对于机器端,它通常是一个 json 响应,包含了http 状态码、错误信息等;对于浏览器端,它通常是一个 “whitelabel” html 错误页面,页面渲染了相同的错误信息。当然,你也可以提供自定义的 html 模板来展示错误信息(下文会说到)。
首先,定制此功能通常涉及利用现有机制,但要替换或增加错误内容,你可以添加 errorattributes 类型的 bean。
若要更改错误处理行为,可以实现 errorwebexceptionhandler 并注册该类型的 bean 定义,但是 webexceptionhandler 级别很低。因此 spring boot 还提供了一种方便的方式,即继承 abstracterrorwebexceptionhandler,让你可以通过 webflux 的方式处理错误,如以下示例所示(这个配置贼复杂,建议还是乖乖的用默认配置吧):
public class customerrorwebexceptionhandler extends abstracterrorwebexceptionhandler { // define constructor here @override protected routerfunction<serverresponse> getroutingfunction(errorattributes errorattributes) { return routerfunctions .route(apredicate, ahandler) .androute(anotherpredicate, anotherhandler); } }
如果你想要为给定的错误码展示自定义的 html 错误页面,你可以在 /error 目录下添加一个错误页面文件。可以是静态html(即添加到任意静态资源文件夹下),也可以使用模板构建,文件名应为确切的状态码或系列掩码。
例如,要映射 404 错误码到静态 html 文件,您的文件夹结构如下:
src/ +- main/ +- java/ | + <source code> +- resources/ +- public/ +- error/ | +- 404.html +- <other public assets>
使用 mustache 模板对 5xx 错误码作映射,您的文件夹结构如下:
src/ +- main/ +- java/ | + <source code> +- resources/ +- templates/ +- error/ | +- 5xx.mustache +- <other templates>
8. 过滤器
spring webflux 提供了一个 webfilter 接口,用来对 http 请求-响应路由进行过滤,在应用程序上下文中找到的 webfilter bean 将自动用于过滤每个路由!以下是一个简单鉴权的过滤器 demo — 对于 没有 token 参数的请求返回 401 错误:
@component public class customwebfilter implements webfilter { @override public mono<void> filter(serverwebexchange exchange, webfilterchain chain) { serverhttprequest request = exchange.getrequest(); multivaluemap<string, string> queryparams = request.getqueryparams(); if (queryparams == null || stringutils.isempty(queryparams.getfirst("token"))) { map<string, string> resultmap = new hashmap<>(); resultmap.put("code", "401"); resultmap.put("msg", "非法请求"); byte[] datas = new byte[0]; try { datas = new objectmapper().writevalueasbytes(resultmap); } catch (jsonprocessingexception e) { e.printstacktrace(); } serverhttpresponse response = exchange.getresponse(); databuffer buffer = response.bufferfactory().wrap(datas); response.setstatuscode(httpstatus.unauthorized); response.getheaders().add("content-type", "application/json;charset=utf-8"); return response.writewith(mono.just(buffer)); } return chain.filter(exchange).then(mono.fromrunnable(() -> { serverhttpresponse response = exchange.getresponse(); //manipulate the response in some way })); } }
可以通过实现 ordered 接口或使用 @order 注释来设置过滤器的执行顺序(执行顺序是从小到大执行,较高的值被解释为较低的优先级)。spring boot 的自动配置功能已经为你提供了一些内置的过滤器,如下是它们的执行顺序:
web filter | order |
---|---|
metricswebfilter | ordered.highest_precedence + 1 |
webfilterchainproxy (spring security) | -100 |
httptracewebfilter | ordered.lowest_precedence - 10 |
推荐阅读
-
spring boot 文档学习笔记day01
-
跟着官方文档学 SpringBoot 二:使用 spring boot
-
跟着官方文档学 SpringBoot 三:spring boot 特性
-
跟着官方文档学 SpringBoot 三:spring boot 特性
-
spring boot 文档学习笔记day02
-
(转)[Spring Boot]swagger导出API文档
-
Spring Boot中使用Swagger2构建API文档
-
Spring Boot中使用Swagger2构建API文档
-
spring boot实现自动输出word文档功能的实例代码
-
Spring Boot 配置swagger2没有文档解决方案