onErrorResume() won't get called - spring-boot

I'm newbie to spring webflux and reactor I want to have a fallback mechanism when some specific exception occurs and based on my research onErrorResume method does that but it won't get called and I get 500 internal server error instead of my fallback triggered and preventing this error.
Note: I use spring webflux which means it made some changes in the normal behavior of the reactor project
public Mono<Date> getExpirationDateFromToken(String token) {
return getAllClaimsFromToken(token)
.onErrorResume(ExpiredJwtException.class, e -> Mono.just(e.getClaims()))
.map(Claims::getExpiration);
}
public Mono<Claims> getAllClaimsFromToken(String token) {
return Mono.just(Jwts.parserBuilder()
.setSigningKey(Base64.getEncoder().encodeToString(secret.getBytes()))
.build()
.parseClaimsJws(token)
.getBody());
}
and this is the stack trace
io.jsonwebtoken.ExpiredJwtException: JWT expired at 2020-03-22T08:17:15Z. Current time: 2020-03-22T08:50:56Z, a difference of 2021835 milliseconds. Allowed clock skew: 0 milliseconds.
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:439) ~[jjwt-impl-0.11.0.jar:0.11.0]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Handler ir.siavash.customerservice.security.controller.AuthController#refreshToken(Mono) [DispatcherHandler]
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP POST "/auth/refreshToken" [ExceptionHandlingWebHandler]
Stack trace:
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:439) ~[jjwt-impl-0.11.0.jar:0.11.0]
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:541) ~[jjwt-impl-0.11.0.jar:0.11.0]
at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:601) ~[jjwt-impl-0.11.0.jar:0.11.0]
at io.jsonwebtoken.impl.ImmutableJwtParser.parseClaimsJws(ImmutableJwtParser.java:173) ~[jjwt-impl-0.11.0.jar:0.11.0]
at ir.siavash.customerservice.security.JWTUtil.getAllClaimsFromToken(JWTUtil.java:42) ~[classes/:na]

Ok, On second look, it's clear. That's a common mistake. Reactive components could be difficult to learn, but you're not very far from the solution.
Mono.just takes a pre-computed value as parameter. Therefore, your getAllClaimsFromToken will create a Mono only after all code given to just is executed. That means that your error happens before any reactive component is available for error management (assembly time).
What you could do instead is using Mono#fromCallable to ask Reactor to compute Jwt token on demand, or creating Mono with just initial string, then mapping using your function.
Let's see second solution:
public Mono<Date> getExpirationDateFromToken(String token) {
return Mono.just(token)
.map(this::getAllClaimsFromToken)
.onErrorResume(ExpiredJwtException.class, e -> Mono.just(e.getClaims()))
.map(Claims::getExpiration);
}
public Claims getAllClaimsFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(Base64.getEncoder().encodeToString(secret.getBytes()))
.build()
.parseClaimsJws(token)
.getBody();
}

Related

syn request in webclient cannot use block for Mono in springboot

I am new to reactive and web client and i just doing a project in springboot microsevice.While when setting the ReactiveUserDetail Service which require the use of mono.I find that my synchronous request by webclient give an error.I suspect this is due to the "block" ,but it seems that i cannot eliminate it.
Error:
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-epoll-3
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.5.1.jar:3.5.1]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
*__checkpoint ⇢ HTTP POST "/signin" [ExceptionHandlingWebHandler]
Original Stack Trace:
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.5.1.jar:3.5.1]
at reactor.core.publisher.Mono.block(Mono.java:1710) ~[reactor-core-3.5.1.jar:3.5.1]
My code:
#Service
public class SecurityUserService implements ReactiveUserDetailsService {
#Autowired
private WebClient.Builder webClientBuilder;
#Override
public Mono<UserDetails> findByUsername(String username) {
UserAuthdto result= webClientBuilder.baseUrl("http://USER").build().get() //make syn request
.uri(uriBuilder -> uriBuilder
.path("/User/AuthUser/{username}")
.build(username))
.retrieve()
.bodyToMono(UserAuthdto.class)
.block()
;
//map the userresult into securityuser
if(result!=null){
return Mono.just(new SecurityUser(result));
}
//no userfound
return null;
}
}
Entry point from the other microservice:
#GetMapping("/AuthUser/{username}")
public UserAuthdto getSecurityUser(#PathVariable String username){
return userCoreservice.getSecurityUser(username);
}
Web client bean:
#Bean
public WebClient webClient(){
return WebClient.builder().build();
}
Please help

"could not resolve view" with `#PreAuthorize` and `WebTestClient`

I'm getting this rather weird bug in a test. My controller has the following method:
#Operation(
summary = "Add a new quote",
operationId = "v1AddQuote",
description = "",
responses = [
ApiResponse(responseCode = "201", description = "Created")
]
)
#RequestMapping(
method = [RequestMethod.POST],
value = ["/quote"],
consumes = ["application/json"]
)
#PreAuthorize("hasRole('ADMIN')")
suspend fun v1AddQuote(quoteDto: QuoteDto): ResponseEntity<Unit> {
val entity = quoteRepository.insert(quoteMapper.dtoToEntity(quoteDto))
.awaitSingleOrNull() ?: return ResponseEntity.internalServerError().build()
return ResponseEntity.created(URI.create("/quote/${entity.id}")).build()
}
And I'm testing it with WebTestClient with:
webClient
.mutateWith(mockUser().roles("ADMIN"))
.post()
.uri("/quote")
.contentType(MediaType.APPLICATION_JSON)
.body(Mono.just(dto), QuoteDto::class.java)
.exchange()
.expectStatus()
.isCreated
.expectHeader()
.location("/quote/${entity.id}")
Surprisingly, this fails with:
java.lang.IllegalStateException: Could not resolve view with name 'quote'
Where does this even come from? Some details:
This doesn't happen when doing the same request in a deployed application, only in the test
Just removing #PreAuthorize solves it
The controller method actually runs, the error appears after it has run
Looking at the stack trace it makes me think this is some sort of redirection Spring Security does?
*__checkpoint ⇢ Handler jdk.proxy2.$Proxy126#v1AddQuote(QuoteDto, Continuation) [DispatcherHandler]
*__checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers$MutatorFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers$SetupMutatorFilter [DefaultWebFilterChain]
*__checkpoint ⇢ HTTP POST "/quote" [ExceptionHandlingWebHandler]
In case it's necessary, my Spring Security configuration is:
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
class WebFluxSecurityConfig {
#Bean
fun userDetailsService(): ReactiveUserDetailsService {
val userDetails = User.withDefaultPasswordEncoder()
.username("admin")
.password("admin")
.roles("ADMIN")
.build()
return MapReactiveUserDetailsService(userDetails)
}
#Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http.invoke {
authorizeExchange {
authorize(anyExchange, permitAll)
}
csrf { disable() }
httpBasic { }
formLogin { disable() }
logout { disable() }
}
}
}
The problem was actually mostly unrelated to any of the info I gave on the post, and actually solved it almost by pure chance. The issue is that I was inheriting an OpenAPI-generated interface in my controller. I don't know why this gives such a weird error, but the solution is, apparently, to add this to your security #EnableReactiveMethodSecurity annotation:
#EnableReactiveMethodSecurity(proxyTargetClass = true)
Without the proxyTargetClass = true parameter, stuff breaks in weird ways.

Getting exception during calling API by Eureka server

I've created the microservices with spring boot getting the exception during calling the URL http://localhost:8765/CURRENCY-EXCHANGE/currency-exchange/from/USD/to/INR from the local server
exception details:
java.net.UnknownHostException: failed to resolve 'DESKTOP-HG47DMC' after 2 queries
at io.netty.resolver.dns.DnsResolveContext.finishResolve(DnsResolveContext.java:1013) ~[netty-resolver-dns-4.1.63.Final.jar:4.1.63.Final]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP GET "/CURRENCY-EXCHANGE/currency-exchange/from/USD/to/INR" [ExceptionHandlingWebHandler] Stack trace:
at io.netty.resolver.dns.DnsResolveContext.finishResolve(DnsResolveContext.java:1013) ~[netty-resolver-dns-4.1.63.Final.jar:4.1.63.Final]
at io.netty.resolver.dns.DnsResolveContext.tryToFinishResolve(DnsResolveContext.java:966) ~[netty-resolver-dns-4.1.63.Final.jar:4.1.63.Final]
at io.netty.resolver.dns.DnsResolveContext.query(DnsResolveContext.java:414) ~[netty-resolver-dns-4.1.63.Final.jar:4.1.63.Final]
at io.netty.resolver.dns.DnsResolveContext.onResponse(DnsResolveContext.java:625) ~[netty-resolver-dns-4.1.63.Final.jar:4.1.63.Final]
at io.netty.resolver.dns.DnsResolveContext.access$400(DnsResolveContext.java:63) ~[netty-resolver-dns-4.1.63.Final.jar:4.1.63.Final]
at io.netty.resolver.dns.DnsResolveContext$2.operationComplete(DnsResolveContext.java:458) ~[netty-resolver-dns-4.1.63.Final.jar:4.1.63.Final]
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:578) [netty-common-4.1.63.Final.jar:4.1.63.Final]
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:571) [netty-common-4.1.63.Final.jar:4.1.63.Final]
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:550) [netty-common-4.1.63.Final.jar:4.1.63.Final]
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:491) [netty-common-4.1.63.Final.jar:4.1.63.Final]
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:616) [netty-common-4.1.63.Final.jar:4.1.63.Final]
at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:605) [netty-common-4.1.63.Final.jar:4.1.63.Final]
at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:104) [netty-common-4.1.63.Final.jar:4.1.63.Final]
at io.netty.resolver.dns.DnsQueryContext.trySuccess(DnsQueryContext.java:201) ~[netty-resolver-dns-4.1.63.Final.jar:4.1.63.Final]
here is my application.properties
spring.application.name = api-gateway
server.port = 8765
eureka.client.serviceUrl.defaultZone =http://localhost:8761/eureka
spring.cloud.gateway.discovery.locator.enabled= true
Add this code snippet to your application.properties file of your microservices.
eureka.instance.hostname=localhost
Add this code snippet to your application.properties file of all your microservices(Naming server, API-Gatway and currency-exchange).
eureka.instance.hostname=localhost
if you get 404 response then please wait for 30 - 60 sec.

How to migrate the GlobalMethodSecurityConfiguration to Reactive Spring?

I have overridden SecurityExpressionRoot in my project exposing a method verifying whether the current user has rights to a given resource.
Then I have overriden GlobalMethodSecurityComfiguration.createExpressionHandler() and then createSecurityExpressionRoot() returning the instance of the overriden SecurityExpressionRoot.
This has worked in a servlet scenario, unfortunately, this doesn't seem to work in a reactive scenario.
How do I convert the method security setup below to the reactive scenario?
In my tests I get the following error:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:379) ~[spring-security-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.ui.LogoutPageGeneratingWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.ui.LoginPageGeneratingWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.csrf.CsrfWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers$MutatorFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP GET "/things/1/jobs/1/log" [ExceptionHandlingWebHandler]
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#RequiredArgsConstructor
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
private final ThingsRepository thingsRepository;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new DefaultMethodSecurityExpressionHandler() {
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
DeployPermissionSecurityExpressionRoot root = new ThingsPermissionSecurityExpressionRoot(thingsRepository, authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(getTrustResolver());
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
};
}
My security config:
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
// #formatter:off
http
.csrf().disable()
.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt(jwt ->{
jwt.jwtDecoder(jwtDecoder());
jwt.jwtAuthenticationConverter(customJwtAuthConverter());
})
.and()
.securityContextRepository(NoOpServerSecurityContextRepository.getInstance());
// #formatter:on
return http.build();
}
SecurityExpressionRoot implementation:
class ThingsPermissionSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
private final ThingsRepository ThingsRepository;
private Object filterObject;
private Object returnObject;
private Object target;
ThingsPermissionSecurityExpressionRoot(ThingsRepository thingsRepository, Authentication authentication) {
super(authentication);
this.thingsRepository = thingsRepository;
}
public boolean hasThingsWritePrivilege(Long thingsId) {
Controller:
public class ThingsController {
private final JobPublisherProvider jobPublisherProvider;
#GetMapping("{thingsId}/jobs/{jobId}/log")
#PreAuthorize("hasThingsWritePrivilege(#thingsId)")
public Flux<DataBuffer> retrieveJobLog(#PathVariable String thingsId, #PathVariable int jobId) {
Controller test method
#Test
#WithMockUser(roles = {"ROLE_JAR_W"})
public void logValueProperlyRetrieved() {
It is not possible to define GlobalSecurityConfiguration in a reactive Spring application (current Spring Security version 5.5.2).
In order to have the same functionality, you need to replace methodSecurityExpressionHandler defined in ReactiveMethodSecurityConfiguration with your own method security expression handler.
To do that, you can extend DefaultMethodSecurityExpressionHandler and define it as #Primary bean.
For your case, it is going to be as following.
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
DeployPermissionSecurityExpressionRoot root = new ThingsPermissionSecurityExpressionRoot(thingsRepository, authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(getTrustResolver());
root.setRoleHierarchy(getRoleHierarchy());
root.setDefaultRolePrefix(getDefaultRolePrefix());
return root;
}
}
#Bean
#Primary
public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
return new CustomMethodSecurityExpressionHandler();
}
References:
https://github.com/spring-projects/spring-security/issues/5046#issuecomment-427097710

Error with a JWT Token Reactive Authentication

I am learning about reactive programming and spring-webflux and I am trying to implement a JWT Token Authorization, but I get a runtime error about the ResponseEntity:
open class JwtReactiveAuthenticationManager(
private val userDetailsService: ReactiveUserDetailsService,
private val passwordEncoder: PasswordEncoder
): ReactiveAuthenticationManager {
private val log: Logger = LoggerFactory.getLogger(this.javaClass)
override fun authenticate(authentication: Authentication): Mono<Authentication> {
return if (authentication.isAuthenticated) {
Mono.just(authentication)
} else Mono.just(authentication)
.switchIfEmpty(Mono.error(BadCredentialsException("Invalid Credentials")))
.cast(UsernamePasswordAuthenticationToken::class.java)
.flatMap { authenticationToken: UsernamePasswordAuthenticationToken -> authenticateToken(authenticationToken) }
.publishOn(Schedulers.parallel())
.onErrorResume { Mono.error(BadCredentialsException("Invalid Credentials")) }
.filter { u: UserDetails -> passwordEncoder.matches(authentication.credentials as String, u.password) }
.switchIfEmpty(Mono.error(BadCredentialsException("Invalid Credentials")))
.map { u: UserDetails -> UsernamePasswordAuthenticationToken(authentication.principal, authentication.credentials, u.authorities) }
}
private fun authenticateToken(authenticationToken: UsernamePasswordAuthenticationToken): Mono<UserDetails>? {
val username = authenticationToken.name
log.info("checking authentication for user $username")
if (username != null && SecurityContextHolder.getContext().authentication == null) {
log.info("authenticated user $username, setting security context")
return this.userDetailsService.findByUsername(username)
}
return null
}
}
Controller method invoked:
#GetMapping("")
override fun list(): Flux<ResponseEntity<Exercise>> {
return repo.findAll().map { o -> ResponseEntity(o, HttpStatus.OK) }
}
2020-01-09 17:54:15.790 ERROR 14680 --- [oundedElastic-1] a.w.r.e.AbstractErrorWebExceptionHandler : [e22e00ce] 500 Server Error for HTTP GET "/api/exercises"
java.lang.IllegalArgumentException: Only a single ResponseEntity supported
at org.springframework.util.Assert.isTrue(Assert.java:118) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ? org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
|_ checkpoint ? HTTP GET "/api/exercises" [ExceptionHandlingWebHandler]
Stack trace:
at org.springframework.util.Assert.isTrue(Assert.java:118) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler.handleResult(ResponseEntityResultHandler.java:121) ~[spring-webflux-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.reactive.DispatcherHandler.handleResult(DispatcherHandler.java:169) ~[spring-webflux-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.reactive.DispatcherHandler.lambda$handle$2(DispatcherHandler.java:147) ~[spring-webflux-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.reactive.DispatcherHandler$$Lambda$1353.000000001B373A70.apply(Unknown Source) ~[na:na]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:118) ~[reactor-core-3.3.1.RELEASE.jar:3.3.1.RELEASE]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1630) ~[reactor-core-3.3.1.RELEASE.jar:3.3.1.RELEASE]
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:241) ~[reactor-core-3.3.1.RELEASE.jar:3.3.1.RELEASE]
you can't return Flux<ResponseEntity<Exercise>> as in multiple response entities.
change it to:
#GetMapping("")
override fun list(): Flux<Exercise> {
return repo.findAll()
}
if you wish to stream the data to the calling client, and it will probably go a lot better.

Resources