|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
函数式端点
Spring WebFlux 包含 WebFlux.fn,这是一种轻量级的函数式编程模型,其中使用函数来路由和处理请求,并且契约设计为不可变性。 它是基于注解的编程模型的替代方案,但同样运行在相同的 Reactive Core 基础上。
概述
在WebFlux.fn中,一个HTTP请求是通过HandlerFunction处理的:一个接受ServerRequest并返回延迟的ServerResponse(即Mono<ServerResponse>).
请求和响应对象都有不可变的契约,提供与JDK 8兼容的访问HTTP请求和响应的方式。
HandlerFunction相当于注解编程模型中的@RequestMapping方法的主体。
传入的请求被路由到一个处理函数,该函数接收 RouterFunction: 一个接受 ServerRequest 并返回延迟的 HandlerFunction(即 Mono<HandlerFunction>)的函数。当路由器函数匹配时,返回一个处理函数;否则返回一个空的 Mono。RouterFunction 等价于 @RequestMapping 注解,但主要区别在于路由器函数不仅提供数据,还提供行为。
RouterFunctions.route() 提供了一个路由器构建器,便于创建路由器,
如下例所示:
-
Java
-
Kotlin
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
PersonRepository repository = ...
PersonHandler handler = new PersonHandler(repository);
RouterFunction<ServerResponse> route = route() (1)
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET("/person", accept(APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.build();
public class PersonHandler {
// ...
public Mono<ServerResponse> listPeople(ServerRequest request) {
// ...
}
public Mono<ServerResponse> createPerson(ServerRequest request) {
// ...
}
public Mono<ServerResponse> getPerson(ServerRequest request) {
// ...
}
}
| 1 | 使用route()创建路由器。 |
val repository: PersonRepository = ...
val handler = PersonHandler(repository)
val route = coRouter { (1)
accept(APPLICATION_JSON).nest {
GET("/person/{id}", handler::getPerson)
GET("/person", handler::listPeople)
}
POST("/person", handler::createPerson)
}
class PersonHandler(private val repository: PersonRepository) {
// ...
suspend fun listPeople(request: ServerRequest): ServerResponse {
// ...
}
suspend fun createPerson(request: ServerRequest): ServerResponse {
// ...
}
suspend fun getPerson(request: ServerRequest): ServerResponse {
// ...
}
}
| 1 | 使用协程路由器DSL创建路由器;响应式替代方案也可通过router { }获得。 |
一种运行RouterFunction的方法是将其转换为HttpHandler并通过内置的服务器适配器之一进行安装:
-
RouterFunctions.toHttpHandler(RouterFunction) -
RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)
大多数应用程序可以通过WebFlux Java配置运行,见运行服务器。
处理器函数
ServerRequest 和 ServerResponse 是不可变接口,提供 JDK 8 友好的访问 HTTP 请求和响应的方式。请求和响应都提供了对主体流的 Reactive Streams 回压支持。
请求体由 Reactor Flux 或 Mono 表示。
响应体可以使用任何 Reactive Streams Publisher 来表示,包括 Flux 和 Mono。
有关更多信息,请参见 Reactive 库。
服务器请求
ServerRequest 提供对 HTTP 方法、URI、标头和查询参数的访问,
而主体的访问则通过 body 方法提供。
以下示例将请求正文提取到 Mono<String>:
-
Java
-
Kotlin
Mono<String> string = request.bodyToMono(String.class);
val string = request.awaitBody<String>()
以下示例将正文提取到 Flux<Person>(或在Kotlin中为 Flow<Person>),
其中 Person 对象从某些序列化形式解码,例如JSON或XML:
-
Java
-
Kotlin
Flux<Person> people = request.bodyToFlux(Person.class);
val people = request.bodyToFlow<Person>()
前一个示例是使用更通用的ServerRequest.body(BodyExtractor)的快捷方式,它接受BodyExtractor函数式策略接口。工具类BodyExtractors提供了许多实例的访问。例如,前一个示例也可以写成如下形式:
-
Java
-
Kotlin
Mono<String> string = request.body(BodyExtractors.toMono(String.class));
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class));
val string = request.body(BodyExtractors.toMono(String::class.java)).awaitSingle()
val people = request.body(BodyExtractors.toFlux(Person::class.java)).asFlow()
以下示例展示了如何访问表单数据:
-
Java
-
Kotlin
Mono<MultiValueMap<String, String>> map = request.formData();
val map = request.awaitFormData()
以下示例展示了如何将多部分数据作为映射来访问:
-
Java
-
Kotlin
Mono<MultiValueMap<String, Part>> map = request.multipartData();
val map = request.awaitMultipartData()
以下示例展示了如何以流式处理方式逐个访问多部分数据:
-
Java
-
Kotlin
Flux<PartEvent> allPartEvents = request.bodyToFlux(PartEvent.class);
allPartsEvents.windowUntil(PartEvent::isLast)
.concatMap(p -> p.switchOnFirst((signal, partEvents) -> {
if (signal.hasValue()) {
PartEvent event = signal.get();
if (event instanceof FormPartEvent formEvent) {
String value = formEvent.value();
// handle form field
}
else if (event instanceof FilePartEvent fileEvent) {
String filename = fileEvent.filename();
Flux<DataBuffer> contents = partEvents.map(PartEvent::content);
// handle file upload
}
else {
return Mono.error(new RuntimeException("Unexpected event: " + event));
}
}
else {
return partEvents; // either complete or error signal
}
}));
val parts = request.bodyToFlux<PartEvent>()
allPartsEvents.windowUntil(PartEvent::isLast)
.concatMap {
it.switchOnFirst { signal, partEvents ->
if (signal.hasValue()) {
val event = signal.get()
if (event is FormPartEvent) {
val value: String = event.value();
// handle form field
} else if (event is FilePartEvent) {
val filename: String = event.filename();
val contents: Flux<DataBuffer> = partEvents.map(PartEvent::content);
// handle file upload
} else {
return Mono.error(RuntimeException("Unexpected event: " + event));
}
} else {
return partEvents; // either complete or error signal
}
}
}
}
请注意,PartEvent对象的正文内容必须被完全消耗、转发或释放,以避免内存泄漏。
服务器响应
ServerResponse 提供对 HTTP 响应的访问,并且由于它是不可变的,您可以使用 build 方法来创建它。您可以使用构建器设置响应状态、添加响应头或提供正文。以下示例创建了一个 200 (OK) 响应,带有 JSON 内容:
-
Java
-
Kotlin
Mono<Person> person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class);
val person: Person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(person)
以下示例展示了如何构建一个包含Location标头且没有正文的201(已创建)响应:
-
Java
-
Kotlin
URI location = ...
ServerResponse.created(location).build();
val location: URI = ...
ServerResponse.created(location).build()
根据所使用的编解码器,可以传递提示参数来定制主体的序列化或反序列化方式。例如,指定一个Jackson JSON视图:
-
Java
-
Kotlin
ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView.class).body(...);
ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView::class.java).body(...)
处理器类
我们可以将处理器函数写成 lambda 表达式,如下例所示:
-
Java
-
Kotlin
HandlerFunction<ServerResponse> helloWorld =
request -> ServerResponse.ok().bodyValue("Hello World");
val helloWorld = HandlerFunction<ServerResponse> { ServerResponse.ok().bodyValue("Hello World") }
这很方便,但在应用程序中我们需要多个函数,并且多个内联lambda可能会变得混乱。因此,将相关的处理器函数组合到一个处理器类中是有用的,这个处理器类的作用类似于注解应用中的 @Controller。
例如,以下类公开了一个响应式Person存储库:
-
Java
-
Kotlin
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;
}
public Mono<ServerResponse> listPeople(ServerRequest request) { (1)
Flux<Person> people = repository.allPeople();
return ok().contentType(APPLICATION_JSON).body(people, Person.class);
}
public Mono<ServerResponse> createPerson(ServerRequest request) { (2)
Mono<Person> person = request.bodyToMono(Person.class);
return ok().build(repository.savePerson(person));
}
public Mono<ServerResponse> getPerson(ServerRequest request) { (3)
int personId = Integer.valueOf(request.pathVariable("id"));
return repository.getPerson(personId)
.flatMap(person -> ok().contentType(APPLICATION_JSON).bodyValue(person))
.switchIfEmpty(ServerResponse.notFound().build());
}
}
| 1 | listPeople 是一个处理器函数,它返回存储库中找到的所有 Person 对象的 JSON。 |
| 2 | createPerson 是一个处理器函数,用于存储包含在请求体中的新 Person。请注意, PersonRepository.savePerson(Person) 返回 Mono<Void>:一个空的 Mono,当从请求中读取并存储人员信息后会发出完成信号。因此我们使用 build(Publisher<Void>) 方法在收到完成信号时发送响应。 |
| 3 | getPerson 是一个处理器函数,它返回一个由 id 路径变量标识的单个人员。我们从仓库中检索该 Person 并创建 JSON 响应,如果找到的话。如果没有找到,我们使用 switchIfEmpty(Mono<T>) 返回一个 404 Not Found 响应。 |
class PersonHandler(private val repository: PersonRepository) {
suspend fun listPeople(request: ServerRequest): ServerResponse { (1)
val people: Flow<Person> = repository.allPeople()
return ok().contentType(APPLICATION_JSON).bodyAndAwait(people);
}
suspend fun createPerson(request: ServerRequest): ServerResponse { (2)
val person = request.awaitBody<Person>()
repository.savePerson(person)
return ok().buildAndAwait()
}
suspend fun getPerson(request: ServerRequest): ServerResponse { (3)
val personId = request.pathVariable("id").toInt()
return repository.getPerson(personId)?.let { ok().contentType(APPLICATION_JSON).bodyValueAndAwait(it) }
?: ServerResponse.notFound().buildAndAwait()
}
}
| 1 | listPeople 是一个处理器函数,它返回存储库中找到的所有 Person 对象的 JSON。 |
| 2 | createPerson 是一个处理器函数,用于存储包含在请求体中的新 Person。
请注意,PersonRepository.savePerson(Person) 是一个挂起函数,没有返回类型。 |
| 3 | getPerson 是一个处理器函数,它根据 id 路径变量返回一个单独的人员信息。我们从仓库中检索该 Person 并创建 JSON 响应,如果找到的话。如果没有找到,则返回 404 Not Found 响应。 |
验证
-
Java
-
Kotlin
public class PersonHandler {
private final Validator validator = new PersonValidator(); (1)
// ...
public Mono<ServerResponse> createPerson(ServerRequest request) {
Mono<Person> person = request.bodyToMono(Person.class).doOnNext(this::validate); (2)
return ok().build(repository.savePerson(person));
}
private void validate(Person person) {
Errors errors = new BeanPropertyBindingResult(person, "person");
validator.validate(person, errors);
if (errors.hasErrors()) {
throw new ServerWebInputException(errors.toString()); (3)
}
}
}
| 1 | 创建 Validator 实例。 |
| 2 | 应用验证。 |
| 3 | 对于400响应抛出异常。 |
class PersonHandler(private val repository: PersonRepository) {
private val validator = PersonValidator() (1)
// ...
suspend fun createPerson(request: ServerRequest): ServerResponse {
val person = request.awaitBody<Person>()
validate(person) (2)
repository.savePerson(person)
return ok().buildAndAwait()
}
private fun validate(person: Person) {
val errors: Errors = BeanPropertyBindingResult(person, "person");
validator.validate(person, errors);
if (errors.hasErrors()) {
throw ServerWebInputException(errors.toString()) (3)
}
}
}
| 1 | 创建 Validator 实例。 |
| 2 | 应用验证。 |
| 3 | 对于400响应抛出异常。 |
Handlers can also use the standard bean validation API (JSR-303) by creating and injecting
a global Validator instance based on LocalValidatorFactoryBean.
See Spring Validation.
RouterFunction
Router functions are used to route the requests to the corresponding HandlerFunction.
Typically, you do not write router functions yourself, but rather use a method on the
RouterFunctions utility class to create one.
RouterFunctions.route() (no parameters) provides you with a fluent builder for creating a router function,
whereas RouterFunctions.route(RequestPredicate, HandlerFunction) offers a direct way
to create a router.
通常,建议使用route()构建器,因为它为典型的映射场景提供了方便的快捷方式,而不需要难以发现的静态导入。
例如,路由器函数构建器提供了方法GET(String, HandlerFunction)来创建GET请求的映射;以及POST(String, HandlerFunction)用于POST请求。
除了基于HTTP方法的映射外,路由构建器还提供了一种在映射到请求时引入额外谓词的方法。对于每种HTTP方法,都有一个重载变体,该变体接受一个RequestPredicate作为参数,通过该参数可以表达额外的约束条件。
谓词
你可以编写自己的 RequestPredicate,但是RequestPredicates工具类提供了常用的实现,这些实现基于请求路径、HTTP方法、内容类型等。
以下示例使用请求谓词来根据Accept头创建约束:
-
Java
-
Kotlin
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", accept(MediaType.TEXT_PLAIN),
request -> ServerResponse.ok().bodyValue("Hello World")).build();
val route = coRouter {
GET("/hello-world", accept(TEXT_PLAIN)) {
ServerResponse.ok().bodyValueAndAwait("Hello World")
}
}
你可以通过以下方式组合多个请求谓词:
-
RequestPredicate.and(RequestPredicate)— 两者都必须匹配。 -
RequestPredicate.or(RequestPredicate)— 两者都可以匹配。
许多来自RequestPredicates的谓词是组合的。
例如,RequestPredicates.GET(String)是由RequestPredicates.method(HttpMethod)
和RequestPredicates.path(String)组合而成的。
上面显示的例子还使用了两个请求谓词,因为构建器内部使用
RequestPredicates.GET,并将其与accept谓词组合。
路由
路由函数是按顺序进行评估的:如果第一个路由不匹配,将评估第二个,依此类推。 因此,先声明更具体的路由再声明一般的路由是有意义的。 当以后将路由函数注册为Spring bean时,这一点也很重要。 请注意,这种行为与基于注解的编程模型不同,在基于注解的编程模型中,会自动选择“最具体”的控制器方法。
在使用路由器功能构建器时,所有定义的路由都将组合成一个RouterFunction,该RouterFunction从build()返回。
还有其他方法可以将多个路由器功能组合在一起:
-
add(RouterFunction)在RouterFunctions.route()构建器上 -
RouterFunction.and(RouterFunction) -
RouterFunction.andRoute(RequestPredicate, HandlerFunction)—RouterFunction.and()的快捷方式,其中包含嵌套的RouterFunctions.route()。
以下示例展示了四个路由的组成:
-
Java
-
Kotlin
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
PersonRepository repository = ...
PersonHandler handler = new PersonHandler(repository);
RouterFunction<ServerResponse> otherRoute = ...
RouterFunction<ServerResponse> route = route()
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson) (1)
.GET("/person", accept(APPLICATION_JSON), handler::listPeople) (2)
.POST("/person", handler::createPerson) (3)
.add(otherRoute) (4)
.build();
| 1 | GET /person/{id} 带有匹配JSON的 Accept 标头被路由到
PersonHandler.getPerson |
| 2 | GET /person 带有匹配JSON的 Accept 标头被路由到
PersonHandler.listPeople |
| 3 | POST /person 没有附加谓词时映射到
PersonHandler.createPerson,并且 |
| 4 | otherRoute 是一个在其他地方创建并添加到路由构建中的路由器函数。 |
import org.springframework.http.MediaType.APPLICATION_JSON
val repository: PersonRepository = ...
val handler = PersonHandler(repository);
val otherRoute: RouterFunction<ServerResponse> = coRouter { }
val route = coRouter {
GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson) (1)
GET("/person", accept(APPLICATION_JSON), handler::listPeople) (2)
POST("/person", handler::createPerson) (3)
}.and(otherRoute) (4)
| 1 | GET /person/{id} 带有匹配JSON的 Accept 标头被路由到
PersonHandler.getPerson |
| 2 | GET /person 带有匹配JSON的 Accept 标头被路由到
PersonHandler.listPeople |
| 3 | POST /person 没有附加谓词时映射到
PersonHandler.createPerson,并且 |
| 4 | otherRoute 是一个在其他地方创建并添加到路由构建中的路由器函数。 |
嵌套路由
一组路由函数通常会有一个共同的谓词,例如共享路径。在上面的例子中,这个共同的谓词将是一个匹配/person的路径谓词,被三条路由使用。当使用注解时,你可以通过使用类型级别的@RequestMapping注解来消除这种重复。在WebFlux.fn中,可以通过路由器函数构建器上的path方法来共享路径谓词。例如,上面的例子中的最后几行可以通过使用嵌套路由进行改进:
-
Java
-
Kotlin
RouterFunction<ServerResponse> route = route()
.path("/person", builder -> builder (1)
.GET("/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET(accept(APPLICATION_JSON), handler::listPeople)
.POST(handler::createPerson))
.build();
| 1 | 请注意,第二个参数path是一个接受路由器构建器的消费者。 |
val route = coRouter { (1)
"/person".nest {
GET("/{id}", accept(APPLICATION_JSON), handler::getPerson)
GET(accept(APPLICATION_JSON), handler::listPeople)
POST(handler::createPerson)
}
}
| 1 | 使用协程路由器DSL创建路由器;响应式替代方案也可通过router { }获得。 |
尽管基于路径的嵌套是最常见的,但你可以通过使用构建器上的nest方法来根据任何类型的谓词进行嵌套。
上述代码中仍然包含一些重复的部分,即共享的Accept-header谓词。
我们可以通过结合使用nest方法和accept来进行进一步优化:
-
Java
-
Kotlin
RouterFunction<ServerResponse> route = route()
.path("/person", b1 -> b1
.nest(accept(APPLICATION_JSON), b2 -> b2
.GET("/{id}", handler::getPerson)
.GET(handler::listPeople))
.POST(handler::createPerson))
.build();
val route = coRouter {
"/person".nest {
accept(APPLICATION_JSON).nest {
GET("/{id}", handler::getPerson)
GET(handler::listPeople)
POST(handler::createPerson)
}
}
}
运行服务器
如何在HTTP服务器中运行路由函数?一个简单的选项是使用以下方法之一将路由函数转换为HttpHandler:
-
RouterFunctions.toHttpHandler(RouterFunction) -
RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)
然后可以使用返回的 HttpHandler 与多个服务器适配器配合使用,具体操作请遵循
HttpHandler 获取特定服务器的说明。
一个更典型的选择,也被Spring Boot使用,是通过基于DispatcherHandler的设置来运行,通过DispatcherHandler-based setup 通过WebFlux Config,它使用Spring配置来声明处理请求所需的组件。WebFlux Java配置声明了以下基础设施组件以支持功能性端点:
-
RouterFunctionMapping: 检测Spring配置中的一个或多个RouterFunction<?>bean,对其进行排序,通过RouterFunction.andOther将其组合,并将请求路由到生成的组合RouterFunction。 -
HandlerFunctionAdapter: 简单的适配器,允许DispatcherHandler调用 一个被映射到请求的HandlerFunction。 -
ServerResponseResultHandler: 处理通过调用HandlerFunction并调用ServerResponse的writeTo方法的结果。
上述组件让功能端点适应DispatcherHandler请求处理生命周期,并且(可能)与注解控制器并行运行,如果声明了注解控制器的话。这也是Spring Boot WebFluxStarters启用功能端点的方式。
以下示例展示了一个WebFlux Java配置(参见 DispatcherHandler 了解如何运行它):
-
Java
-
Kotlin
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Bean
public RouterFunction<?> routerFunctionA() {
// ...
}
@Bean
public RouterFunction<?> routerFunctionB() {
// ...
}
// ...
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
// configure message conversion...
}
@Override
public void addCorsMappings(CorsRegistry registry) {
// configure CORS...
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// configure view resolution for HTML rendering...
}
}
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Bean
fun routerFunctionA(): RouterFunction<*> {
// ...
}
@Bean
fun routerFunctionB(): RouterFunction<*> {
// ...
}
// ...
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
// configure message conversion...
}
override fun addCorsMappings(registry: CorsRegistry) {
// configure CORS...
}
override fun configureViewResolvers(registry: ViewResolverRegistry) {
// configure view resolution for HTML rendering...
}
}
过滤处理程序函数
你可以通过使用路由函数构建器上的 before、after 或 filter 方法来过滤处理器函数。通过注解,你可以使用 @ControllerAdvice、一个 ServletFilter 或两者结合来实现类似的功能。过滤器将应用于由构建器构建的所有路由。这意味着在嵌套路由中定义的过滤器不会应用到“顶级”路由上。
例如,考虑以下示例:
-
Java
-
Kotlin
RouterFunction<ServerResponse> route = route()
.path("/person", b1 -> b1
.nest(accept(APPLICATION_JSON), b2 -> b2
.GET("/{id}", handler::getPerson)
.GET(handler::listPeople)
.before(request -> ServerRequest.from(request) (1)
.header("X-RequestHeader", "Value")
.build()))
.POST(handler::createPerson))
.after((request, response) -> logResponse(response)) (2)
.build();
| 1 | 添加自定义请求头的before过滤器仅应用于两个GET路由。 |
| 2 | The after 过滤器会记录响应,它被应用于所有路由,包括嵌套的路由。 |
val route = router {
"/person".nest {
GET("/{id}", handler::getPerson)
GET("", handler::listPeople)
before { (1)
ServerRequest.from(it)
.header("X-RequestHeader", "Value").build()
}
POST(handler::createPerson)
after { _, response -> (2)
logResponse(response)
}
}
}
| 1 | 添加自定义请求头的before过滤器仅应用于两个GET路由。 |
| 2 | The after 过滤器会记录响应,它被应用于所有路由,包括嵌套的路由。 |
The filter 方法在路由构建器上接受一个 HandlerFilterFunction: 一个接受 ServerRequest 和 HandlerFunction 并返回一个 ServerResponse 的函数。该处理器函数参数表示链中的下一个元素。这通常是被路由到的处理器,但也可能是应用了多个过滤器时的另一个过滤器。
现在我们可以为我们的路由添加一个简单的安全过滤器,假设我们有一个SecurityManager可以判断特定路径是否允许。
以下示例展示了如何实现这一点:
-
Java
-
Kotlin
SecurityManager securityManager = ...
RouterFunction<ServerResponse> route = route()
.path("/person", b1 -> b1
.nest(accept(APPLICATION_JSON), b2 -> b2
.GET("/{id}", handler::getPerson)
.GET(handler::listPeople))
.POST(handler::createPerson))
.filter((request, next) -> {
if (securityManager.allowAccessTo(request.path())) {
return next.handle(request);
}
else {
return ServerResponse.status(UNAUTHORIZED).build();
}
})
.build();
val securityManager: SecurityManager = ...
val route = router {
("/person" and accept(APPLICATION_JSON)).nest {
GET("/{id}", handler::getPerson)
GET("", handler::listPeople)
POST(handler::createPerson)
filter { request, next ->
if (securityManager.allowAccessTo(request.path())) {
next(request)
}
else {
status(UNAUTHORIZED).build();
}
}
}
}
上一个示例演示了调用 next.handle(ServerRequest) 是可选的。
我们只在允许访问时才运行处理器函数。
除了使用路由器功能构建器上的 filter 方法,还可以通过 RouterFunction.filter(HandlerFilterFunction) 将过滤器应用于现有的路由器功能。
CORS支持通过专门的CorsWebFilter为功能性端点提供。 |